Summation at individual and overall level - tsql

I have something as
Looking for an output as
I was trying with RollUp, cube, Grouping Set but nothing seems to be fitting properly.
Here is my unsuccessful attempt:
declare #t table(
[Employee Name] varchar(50),Bucket int,
[Start Inventory No] int ,[Start Inventory Amount] int,
[No Of Promise to Pay] int,[Promise to Pay Amount] int)
insert into #t
select 'A', 0,10,10000,3,100 union all
select 'A', 1,20,20000,7,500 union all
select 'B', 0,45,90000,4,200 union all
select 'B', 1,12,70000,6,600 union all
select 'c', 0,16,19000,1,500 union all
select 'c', 1,56,9000,10,2500
select
[Employee Name]
,Bucket=case when x.rn= 11 then 'total' else Bucket end
,[Start Inventory No]= case when x.rn= 11 then sum([Start Inventory No]) else [Start Inventory No] end
from
(select
rn=ROW_NUMBER() Over(partition by [Employee Name] order by (select 1)),
*
from #t
GROUP BY
Rollup
([Employee Name] ,Bucket,[Start Inventory No],[Start Inventory Amount],[No Of Promise to Pay],
[Promise to Pay Amount]))X where x.Rn in (1,6,11)
group by [Employee Name]
,Bucket, rn

This should be done with a pivot table on the client, not on the server.
If for some reason you do want to get to the second table from the first, I would do it as
select
case when grouping(fake_column) = 1 then null else [Employee Name] end as [Employee Name],
case when grouping([Employee Name]) = 1 and grouping(fake_column) = 1 then 'Gran Total' when grouping(fake_column) = 1 then 'Total' else cast(sum(Bucket) as varchar) end as Bucket,
sum([Start Inventory No]) as [Start Inventory No],
sum([Start Inventory Amount]) as [Start Inventory Amount],
sum([No Of Promise to Pay]) as [No Of Promise to Pay],
sum([Promise to Pay Amount]) as [Promise to Pay Amount]
from
(select *, row_number() over(partition by [Employee Name] order by 1/0) as fake_column from #t) data
group by
rollup([Employee Name], fake_column)
;
The idea is that you make each row unique by introducing a fake column, and include that column in the grouping, so that the original rows come out as 'grouped' results too (each 'group' contains one row due to the unique number).

Related

SQL Server - Select with Group By together Raw_Number

I'm using SQL Server 2000 (80). So, it's not possible to use the LAG function.
I have a code a data set with four columns:
Purchase_Date
Facility_no
Seller_id
Sale_id
I need to identify missing Sale_ids. So every sale_id is a 100% sequential, so the should not be any gaps in order.
This code works for a specific date and store if specified. But i need to work on entire data set looping looping through every facility_id and every seller_id for ever purchase_date
declare #MAXCOUNT int
set #MAXCOUNT =
(
select MAX(Sale_Id)
from #table
where
Facility_no in (124) and
Purchase_date = '2/7/2020'
and Seller_id = 1
)
;WITH TRX_COUNT AS
(
SELECT 1 AS Number
union all
select Number + 1 from TRX_COUNT
where Number < #MAXCOUNT
)
select * from TRX_COUNT
where
Number NOT IN
(
select Sale_Id
from #table
where
Facility_no in (124)
and Purchase_Date = '2/7/2020'
and seller_id = 1
)
order by Number
OPTION (maxrecursion 0)
My Dataset
This column:
case when
Sale_Id=0 or 1=Sale_Id-LAG(Sale_Id) over (partition by Facility_no, Purchase_Date, Seller_id)
then 'OK' else 'Previous Missing' end
will tell you which Seller_Ids have some sale missing. If you want to go a step further and have exactly your desired output, then filter out and distinct the 'Previous Missing' ones, and join with a tally table on not exists.
Edit: OP mentions in comments they can't use LAG(). My suggestion, then, would be:
Make a temp table that that has the max(sale_id) group by facility/seller_id
Then you can get your missing results by this pseudocode query:
Select ...
from temptable t
inner join tally N on t.maxsale <=N.num
where not exists( select ... from sourcetable s where s.facility=t.facility and s.seller=t.seller and s.sale=N.num)
> because the only way to "construct" nonexisting combinations is to construct them all and just remove the existing ones.
This one worked out
; WITH cte_Rn AS (
SELECT *, ROW_NUMBER() OVER(PARTITION BY Facility_no, Purchase_Date, Seller_id ORDER BY Purchase_Date) AS [Rn_Num]
FROM (
SELECT
Facility_no,
Purchase_Date,
Seller_id,
Sale_id
FROM MyTable WITH (NOLOCK)
) a
)
, cte_Rn_0 as (
SELECT
Facility_no,
Purchase_Date,
Seller_id,
Sale_id,
-- [Rn_Num] AS 'Skipped Sale'
-- , case when Sale_id = 0 Then [Rn_Num] - 1 Else [Rn_Num] End AS 'Skipped Sale for 0'
, [Rn_Num] - 1 AS 'Skipped Sale for 0'
FROM cte_Rn a
)
SELECT
Facility_no,
Purchase_Date,
Seller_id,
Sale_id,
-- [Skipped Sale],
[Skipped Sale for 0]
FROM cte_Rn_0 a
WHERE NOT EXISTS
(
select * from cte_Rn_0 b
where b.Sale_id = a.[Skipped Sale for 0]
and a.Facility_no = b.Facility_no
and a.Purchase_Date = b.Purchase_Date
and a.Seller_id = b.Seller_id
)
--ORDER BY Purchase_Date ASC

Checking Slowly Changing Dimension 2

I have a table that looks like this:
A slowly changing dimension type 2, according to Kimball.
Key is just a surrogate key, a key to make rows unique.
As you can see there are three rows for product A.
Timelines for this product are ok. During time the description of the product changes.
From 1-1-2020 up until 4-1-2020 the description of this product was ProdA1.
From 5-1-2020 up until 12-2-2020 the description of this product was ProdA2 etc.
If you look at product B, you see there are gaps in the timeline.
We use DB2 V12 z/Os. How can I check if there are gaps in the timelines for each and every product?
Tried this, but doesn't work
with selectie (key, tel) as
(select product, count(*)
from PROD_TAB
group by product
having count(*) > 1)
Select * from
PROD_TAB A
inner join selectie B
on A.product = B.product
Where not exists
(SELECT 1 from PROD_TAB C
WHERE A.product = C.product
AND A.END_DATE + 1 DAY = C.START_DATE
)
Does anyone know the answer?
The following query returns all gaps for all products.
The idea is to enumerate (RN column) all periods inside each product by START_DATE and join each record with its next period record.
WITH
/*
MYTAB (PRODUCT, DESCRIPTION, START_DATE, END_DATE) AS
(
SELECT 'A', 'ProdA1', DATE('2020-01-01'), DATE('2020-01-04') FROM SYSIBM.SYSDUMMY1
UNION ALL SELECT 'A', 'ProdA2', DATE('2020-01-05'), DATE('2020-02-12') FROM SYSIBM.SYSDUMMY1
UNION ALL SELECT 'A', 'ProdA3', DATE('2020-02-13'), DATE('2020-12-31') FROM SYSIBM.SYSDUMMY1
UNION ALL SELECT 'B', 'ProdB1', DATE('2020-01-05'), DATE('2020-01-09') FROM SYSIBM.SYSDUMMY1
UNION ALL SELECT 'B', 'ProdB2', DATE('2020-01-12'), DATE('2020-03-14') FROM SYSIBM.SYSDUMMY1
UNION ALL SELECT 'B', 'ProdB3', DATE('2020-03-15'), DATE('2020-04-18') FROM SYSIBM.SYSDUMMY1
UNION ALL SELECT 'B', 'ProdB4', DATE('2020-04-16'), DATE('2020-05-03') FROM SYSIBM.SYSDUMMY1
)
,
*/
MYTAB_ENUM AS
(
SELECT
T.*
, ROWNUMBER() OVER (PARTITION BY PRODUCT ORDER BY START_DATE) RN
FROM MYTAB T
)
SELECT A.PRODUCT, A.END_DATE + 1 START_DT, B.START_DATE - 1 END_DT
FROM MYTAB_ENUM A
JOIN MYTAB_ENUM B ON B.PRODUCT = A.PRODUCT AND B.RN = A.RN + 1
WHERE A.END_DATE + 1 <> B.START_DATE
AND A.END_DATE < B.START_DATE;
The result is:
|PRODUCT|START_DT |END_DT |
|-------|----------|----------|
|B |2020-01-10|2020-01-11|
May be more efficient way:
WITH MYTAB2 AS
(
SELECT
T.*
, LAG(END_DATE) OVER (PARTITION BY PRODUCT ORDER BY START_DATE) END_DATE_PREV
FROM MYTAB T
)
SELECT PRODUCT, END_DATE_PREV + 1 START_DATE, START_DATE - 1 END_DATE
FROM MYTAB2
WHERE END_DATE_PREV + 1 <> START_DATE
AND END_DATE_PREV < START_DATE;
Thnx Mark, will try this one of these days.
Never heard of LAG in DB2 V12 for z/Os
Will read about it
Thnx

Return Results on Right Outer Join on empty table with where condtion

So I'm trying to return 12 months of data from billing_monthly_verizon_charges_detail_archive, and I can't find a way to make to return 0s when there is no data without removing the condition verizon.ITEM_DESCRIPTION <> 'Payment Received'. I get why it's not returning any data, but how can I make it return 0 for each of the months invoice_date table with the conditional - which is needed for when there is data.
SELECT 'H. Verizon' AS category, CONVERT(VARCHAR(7), id.INVOICE_DATE, 111) AS invoice_date, ISNULL(SUM(verizon.COST), 0.00) AS total_charges
FROM BILLING_MONTHLY_VERIZON_CHARGES_DETAIL_ARCHIVE AS verizon
RIGHT OUTER JOIN
INVOICE_DATES AS id ON verizon.BILL_CYCLE_DATE = id.INVOICE_DATE
WHERE(id.INVOICE_DATE BETWEEN #BillingMonthMinus13 AND #BillingMonth) AND (verizon.ITEM_DESCRIPTION <> 'Payment Received')
GROUP BY id.INVOICE_DATE
COALESCE(verizon.ITEM_DESCRIPTION,'') <> 'Payment Received'
consider:
SELECT
'H. Verizon' AS category,
CONVERT(VARCHAR(7), INVOICE_DATE,111) AS invoice_date,
ISNULL(SUM(COST), 0.00) AS total_charges
FROM
(SELECT
INVOICE_DATE
FROM
INVOICE_DATES
WHERE
INVOICE_DATE BETWEEN #BillingMonthMinus13 AND #BillingMonth ) id LEFT OUTER JOIN
(SELECT
BILL_CYCLE_DATE,
COST
FROM
BILLING_MONTHLY_VERIZON_CHARGES_DETAIL_ARCHIVE
WHERE
ITEM_DESCRIPTION <> 'Payment Received' ) verizon ON
BILL_CYCLE_DATE = INVOICE_DATE
GROUP BY
INVOICE_DATE

postgres - get top category purchased by customer

I have a denormalized table with the columns:
buyer_id
order_id
item_id
item_price
item_category
I would like to return something that returns 1 row per buyer_id
buyer_id, sum(item_price), item_category
-- but ONLY for the category with the highest rank of sales along that specific buyer_id.
I can't get row_number() or partition to work because I need to order by the sum of item_price relative to item_category relative to buyer. Am I overlooking anything obvious?
You need a few layers of fudging here:
SELECT buyer_id, item_sum, item_category
FROM (
SELECT buyer_id,
rank() OVER (PARTITION BY buyer_id ORDER BY item_sum DESC) AS rnk,
item_sum, item_category
FROM (
SELECT buyer_id, sum(item_price) AS item_sum, item_category
FROM my_table
GROUP BY 1, 3) AS sub2) AS sub
WHERE rnk = 1;
In sub2 you calculate the sum of 'item_price' for each 'item_category' for each 'buyer_id'. In sub you rank these with a window function by 'buyer_id', ordering by 'item_sum' in descending order (so the highest 'item_sum' comes first). In the main query you select those rows where rnk = 1.

Find high and low prices in sql

In this sample database there are two tables, products and prices.
The goal is to find the highest and the lowest price for each product.
The price table can have either zero, one or two rows per product.
create table products(
id int,
name nvarchar(50)
)
create table prices(
productId int,
price int
)
insert into products (id, name) values (33,'bike')
insert into products (id, name) values (44,'car')
insert into products (id, name) values (55,'bus')
insert into prices (productId, price) values (33, 10)
insert into prices (productId, price) values (33, 40)
insert into prices (productId, price) values (44, 300)
The sql query should result in this:
productId highPrice lowPrice
33 40 10
44 300 NULL
55 NULL NULL
This is for MySQL, but it might work for you too.
SELECT
products.id as productId
, MIN(price) as highPrice
, MAX(price) as lowPrice
FROM products
LEFT JOIN prices ON products.id=prices.productId
GROUP BY products.id
SELECT productId,
MAX(price) AS highPrice,
MIN(price) AS lowPrice
FROM prices
GROUP BY productId
and if you want the product name in there as well:
SELECT name,
MAX(price) AS highPrice,
MIN(price) AS lowPrice
FROM products
LEFT OUTER JOIN prices ON ID = ProductID
GROUP BY name
This gives you the table that you're looking for (I notice that the other answers don't), in SQL Server 2005
select P.ID as ProductID,
nullif(sum(case when idx=1 then price else 0 end), 0) as highPrice,
nullif(sum(case when idx=2 then price else 0 end), 0) as lowPrice from
(
select productid, price, row_number() over(partition by productID order by price desc) as idx from prices
) T
right join products P on T.productID = P.ID
group by P.ID