sql pivot on multiple values - sql

Given the following data table
Id Code Date PIVOT VALUE1 VALUE2
1 WMAZ 2014-01-31 12:23:06.000 1 103 1
2 EEEE 2014-01-31 11:59:15.000 2 74 2
3 WMAZ 2014-01-31 11:59:10.000 1 3 3
4 WMAZ 2014-01-31 11:56:55.000 2 10 4
5 WMAZ 2014-01-31 11:56:14.000 2 96 5
6 EEEE 2014-01-31 11:55:26.000 2 159 6
I need to pivot the data to get this:
Code Date SUMVALUE1FORPIVOT1 SUMVALUE1FORPIVOT2 SUMVALUE2FORPIVOT1 SUMVALUE2PIVOT2
WMAZ 2014-01-31 (103+3) (10+96) (1+3) (4+5)
EEEE 2014-01-31 NULL (74+159) NULL (6+2)
How do I get a sum for value1 and value2 for each pivot grouped by Code and Date without writing a sub-query for each field?

You could do something like this:
Test data
DECLARE #tbl TABLE(Id INT,Code VARCHAR(100),
Date DATETIME,
[PIVOT] INT,
VALUE1 INT,
VALUE2 INT)
INSERT INTO #tbl
VALUES
(1,'WMAZ','2014-01-31 12:23:06.000',1,103,1),
(2,'EEEE','2014-01-31 11:59:15.000',2,74,2),
(3,'WMAZ','2014-01-31 11:59:10.000',1,3,3),
(4,'WMAZ','2014-01-31 11:56:55.000',2,10,4),
(5,'WMAZ','2014-01-31 11:56:14.000',2,96,5),
(6,'EEEE','2014-01-31 11:55:26.000',2,159,6)
Query
SELECT
tbl.Code,
CAST(tbl.Date AS DATE) AS Date,
SUM(CASE WHEN [PIVOT]=1 THEN VALUE1 ELSE NULL END) AS SUMVALUE1FORPIVOT1,
SUM(CASE WHEN [PIVOT]=2 THEN VALUE1 ELSE NULL END) AS SUMVALUE1FORPIVOT2,
SUM(CASE WHEN [PIVOT]=1 THEN VALUE2 ELSE NULL END) AS SUMVALUE2FORPIVOT1,
SUM(CASE WHEN [PIVOT]=2 THEN VALUE2 ELSE NULL END) AS SUMVALUE2FORPIVOT2
FROM
#tbl AS tbl
GROUP BY
tbl.Code,
CAST(tbl.Date AS DATE)
ORDER BY
tbl.Code DESC
Result:
Code Date SUMVALUE1FORPIVOT1 SUMVALUE1FORPIVOT2 SUMVALUE2FORPIVOT1 SUMVALUE2PIVOT2
---------------------------------------------------------------------------------------------------
WMAZ 2014-01-31 106 106 4 9
EEEE 2014-01-31 NULL 233 NULL 8

Related

How to update a column based on values of other columns

I have a tables as below
row_wid id code sub_code item_nbr orc_cnt part_cnt variance reporting_date var_start_date
1 1 ABC PQR 23AB 0 1 1 11-10-2019 NULL
2 1 ABC PQR 23AB 0 1 1 12-10-2019 NULL
3 1 ABC PQR 23AB 1 1 0 13-10-2019 NULL
4 1 ABC PQR 23AB 1 2 1 14-10-2019 NULL
5 1 ABC PQR 23AB 1 3 2 15-10-2019 NULL
I have to update var_start_date column with min(reporting_date) for each combination of id,code,sub_code and item_nbr only till variance field is zero.
Row with variance = 0 should have null var_start_date. and next row after that should have next min(var_start_date.). FYI, variance is calculated as par_cnt-orc_cnt
so my output should look like this -
row_wid id code sub_code item_nbr orc_cnt part_cnt variance reporting_date var_start_date
1 1 ABC PQR 23AB 0 1 1 11-10-2019 11-10-2019
2 1 ABC PQR 23AB 0 1 1 12-10-2019 11-10-2019
3 1 ABC PQR 23AB 1 1 0 13-10-2019 NULL
4 1 ABC PQR 23AB 1 2 1 14-10-2019 14-10-2019
5 1 ABC PQR 23AB 1 3 2 15-10-2019 14-10-2019
I am trying to write a function using below query to divide the data into sets.
SELECT DISTINCT MIN(reporting_date)
OVER (partition by id, code,sub_code,item_nbr ORDER BY row_wid ),
RANK() OVER (partition by id, code,sub_code,item_nbr ORDER BY row_wid)
AS rnk,id, code,sub_code,item_nbr,orc_cnt,part_cnt,variance,row_wid
FROM TABLE T1
.But dont know how to include variance field to split the sets.
I would suggest:
select t.*,
(case when variance <> 0
then min(reporting_date) over (partition by id, code, sub_code, item_nbr, grouping)
end) as new_reporting_date
from (select t.*,
sum(case when variance = 0 then 1 else 0 end) over (partition by id, code, sub_code, item_nbr) as grouping
from t
) t;
Note that this does not use a JOIN. It should be more efficient than an answer that does.
Try as below
SELECT T.*, CASE WHEN T.variance = 0 THEN NULL ELSE MIN(reporting_date) OVER (PARTITION BY T1.RANK ORDER BY T1.RANK) END AS New_var_start_date
FROM mytbl T
LEFT JOIN (
SELECT row_wid, variance, COUNT(CASE variance WHEN 0 THEN 1 END) OVER (ORDER BY row_wid ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) +1 AS [Rank]
FROM mytbl
) T1 ON T.row_wid = T1.row_wid
SQL FIDDLE DEMO

SQL Server - complete results with non existent data

I have a table where I have all the customers and a table where I have all their restrictions.
CUSTOMER
customer_id customer_name
1 name 1
2 name 2
CUSTOMER_RESTRICTIONS
rest_type day_of_week hour_start hour_stop customer_id
TYPE1 0 08:00 12:00 1
TYPE1 0 13:00 17:00 1
TYPE2 0 17:00 23:59 1
Problem: I only have a record for a restriction type and a customer when the customer has a restriction and this is a problem in the visualization I want to build.
I need every customer, every day and every restriction type, even when there is no restriction. In that case hour_start and hour_stop would be NULL.
For the tables shown, the output would be
rest_type day_of_week hour_start hour_stop customer_id
TYPE1 0 08:00 12:00 1
TYPE1 0 08:00 12:00 1
TYPE1 1 NULL NULL 1
TYPE1 2 NULL NULL 1
TYPE1 3 NULL NULL 1
TYPE1 4 NULL NULL 1
TYPE1 5 NULL NULL 1
TYPE1 6 NULL NULL 1
TYPE1 1 NULL NULL 1
TYPE1 2 NULL NULL 1
TYPE1 3 NULL NULL 1
TYPE1 4 NULL NULL 1
TYPE1 5 NULL NULL 1
TYPE2 0 NULL NULL 1
TYPE2 1 NULL NULL 1
TYPE2 2 NULL NULL 1
TYPE2 3 NULL NULL 1
TYPE2 4 NULL NULL 1
TYPE2 5 NULL NULL 1
TYPE2 6 NULL NULL 1
TYPE1 0 NULL NULL 2
TYPE1 1 NULL NULL 2
TYPE1 2 NULL NULL 2
TYPE1 3 NULL NULL 2
TYPE1 4 NULL NULL 2
TYPE1 5 NULL NULL 2
TYPE1 6 NULL NULL 2
TYPE2 0 NULL NULL 2
TYPE2 1 NULL NULL 2
TYPE2 2 NULL NULL 2
TYPE2 3 NULL NULL 2
TYPE2 4 NULL NULL 2
TYPE2 5 NULL NULL 2
TYPE2 6 NULL NULL 2
How can I achieve that? I couldn't even start to build this query.
Essentially you need to start with the data you must have and left join the optional data. E.g., something like this:
select c.customer_id
,r.[rest_type]
,d.[day_of_week]
,r.[hour_start]
,r.[hour_stop]
from CUSTOMER c
cross apply (
select 0 as day_of_week
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
) d
left join CUSTOMER_RESTRICTIONS r on c.customer_id = r.customer_id and d.day_of_week = r.day_of_week
Output:
customer_id rest_type day_of_week hour_start hour_stop
----------- --------- ----------- ---------- ---------
1 TYPE1 0 08:00 12:00
1 TYPE1 0 13:00 17:00
1 TYPE2 0 17:00 23:59
1 NULL 1 NULL NULL
1 NULL 2 NULL NULL
1 NULL 3 NULL NULL
1 NULL 4 NULL NULL
1 NULL 5 NULL NULL
1 NULL 6 NULL NULL
If there are only type rest_types, you don't have a lookup table for them, and you want to show a row for each, you would do:
select c.customer_id
,t.[rest_type]
,d.[day_of_week]
,r.[hour_start]
,r.[hour_stop]
from CUSTOMER c
cross apply (
select 0 as day_of_week
union all select 1
union all select 2
union all select 3
union all select 4
union all select 5
union all select 6
) d
cross apply (
select 'TYPE1' as rest_type
union all select 'TYPE2'
) t
left join CUSTOMER_RESTRICTIONS r on c.customer_id = r.customer_id
and d.day_of_week = r.day_of_week
and t.rest_type = r.rest_type
(select rest_type, day_of_week,
hour_start ,
hour_stop
from table A
where rest_type IS NOT NULL)
Union
(select rest_type,
day_of_week,
NULL ,NULL
from table A
where rest_type IS NULL)
Is this what you want ?
First off, I wouldn't store rest type as you are, that is a bad habit, it should be a reference table!
You need to cross apply to get all your possible combinations, and then add in the values you DO have...
DECLARE #Customer TABLE (Id INT IDENTITY(1,1), Name NVARCHAR(100))
DECLARE #Rest TABLE (Id INT IDENTITY(1,1), Name NVARCHAR(100))
DECLARE #Restrictions TABLE (Id INT IDENTITY(1,1), RestID INT, CustomerID INT, Day_of_Week TINYINT, hour_start TIME, hour_end TIME)
INSERT INTO #Customer (NAME)
VALUES('JOHN'),('SUSAN')
INSERT INTO #Rest (NAME)
VALUES ('TYPE A'),('TYPE B')
INSERT INTO #Restrictions (RestID,CustomerID,Day_of_Week,hour_start,hour_end)
VALUES (1,1,0,'08:00','12:00'),
(1,1,0,'13:00','17:00'),
(1,2,0,'17:00','23:59')
;WITH DaysofWeek AS
(
SELECT 0 AS dow
UNION ALL
SELECT dow+1
FROM DaysofWeek
WHERE dow<5
)
SELECT *
FROM #Customer C
CROSS APPLY #Rest R
CROSS APPLY DaysofWeek D
LEFT JOIN #Restrictions X
ON X.Day_of_Week=D.dow
AND X.CustomerID=C.Id
AND X.RestID=R.Id
ORDER BY C.Id, D.dow, R.Id

oracle select the rows using analytical conditions

I have a table with the data as below
id start_dt cance1_dt record_eff_dt latest_row_flag
1 null null 01/01/2018 N
1 01/02/2018 01/02/2018 01/02/2018 N
1 01/03/2018 null 01/03/2018 Y
2 null 01/04/2018 01/04/2018 Y
3 01/05/2018 null 01/05/2018 N
3 null 01/06/2018 01/06/2018 Y
I have to rank the rows by grouping the rows with same id (partition by id) using the below conditions
condition 1
case when start_dt is not null AND cancel_dt is null and latest_row_flag = 'Y' then rank is 1.
condition 2
case when cancel_dt is null and latest_row_flag = 'Y' then scan all the rows for that same id and see if there is ever a row with start_dt is not null and cancel_dt is null then rank it as 2 else 3.
condition 3
else rank all other cases with 3
I'm struggling to come up with the code for condition 2, where i have to look through all the previous rows to see if there is ever such a case. Please help.
Hmmm . . . the condition just depends on window functions:
select t.*,
(case when start_dt is not null and cancel_dt is null and latest_row_flag = 'Y'
then 1
when cancel_dt is null and latest_row_flag = 'Y' and
sum(case when start_dt is not null and cancel_dt is null then 1 else 0 end) over (partition by id) > 0
then 2
else 3
end) as ranking
from t;

PIVOT values from two columns to multiple columns

Table: Sample
ID Day Status MS
----------------------------
1 1 0 10
1 2 0 20
1 3 1 15
2 3 1 3
2 30 0 5
2 31 0 6
Expected Result:
ID Day1 Day2 Day3....Day30 Day31 Status1 Status2 Status3...Status30 Status31
---------------------------------------------------------------------------------------
1 10 20 15 NULL NULL 0 0 1 NULL NULL
2 NULL NULL 3 5 6 NULL NULL 1 0 0
I want to get the MS and Status value for each day from 1 to 31 for each ID.
I have used PIVOT to get the below result.
Result:
ID Day1 Day2 Day3....Day30 Day31
-------------------------------------
1 10 20 15 NULL NULL
2 NULL NULL 3 5 6
Query:
SELECT
ID
,[1] AS Day1
,[2] AS Day2
,[3] AS Day3
.
.
.
,[30] AS Day30
,[31] AS Day31
FROM
(
SELECT
ID
,[Day]
,MS
FROM
Sample
) AS A
PIVOT
(
MIN(MS)
FOR [Day] IN([1],[2],[3],...[30],[31])
) AS pvtTable
How can I merge the Status column with the result?.
Try this. Use Another Pivot to transpose Status column. Then use aggregate (Max or Min) in select column list with group by Id to get the Result.
CREATE TABLE #est
(ID INT,[Day] INT,[Status] INT,MS INT)
INSERT #est
VALUES (1,1,0,10),(1,2,0,20),(1,3,1,15 ),
(2,3,1,3),(2,30,0,5),(2,31,0,6)
SELECT ID,
Max([Day1]) [Day1],
Max([Day2]) [Day2],
Max([Day3]) [Day3],
Max([Day30]) [Day30],
Max([Day31]) [Day31],
Max([status1]) [status1],
Max([status2]) [status2],
Max([status3]) [status3],
Max([status30])[status30],
Max([status31])[status31]
FROM (SELECT Id,
'status' + CONVERT(VARCHAR(30), Day) col_stat,
'Day' + CONVERT(VARCHAR(30), Day) Col_Day,
[status],
ms
FROM #est) a
PIVOT (Min([ms])
FOR Col_Day IN([Day1],[Day2],[Day3],[Day30],[Day31])) piv
PIVOT (Min([status]) FOR col_stat IN ([status1],[status2],[status3],[status30],[status31])) piv1
GROUP BY id

Cross apply to fill down values with multiple columns

I have a table with a few columns. I want to fill down values to replace nulls, but this is complicated by the additional columns. Here is a sample of what I have:
date id1 id2 id3 id4 value
1/1/14 a 1 1 1 1.2
1/2/14 a 1 1 1 NULL
1/8/14 a 1 1 1 2.3
1/1/14 a 2 1 1 10.1
1/2/14 a 2 1 1 12.3
1/17/14 a 2 1 1 NULL
1/18/14 a 2 1 1 10.8
1/1/14 a 2 3 1 100.3
1/2/14 a 2 3 1 NULL
1/6/14 a 2 3 1 110.4
I want to copy down value while the value remains within a "group" of id1-4. For example, all of the "A-1-1-1" should be isolated from "a-2-1-1" in terms of what values to copy down. The output I need is:
date id1 id2 id3 id4 value
1/1/14 a 1 1 1 1.2
1/2/14 a 1 1 1 1.2
1/8/14 a 1 1 1 2.3
1/1/14 a 2 1 1 10.1
1/2/14 a 2 1 1 12.3
1/17/14 a 2 1 1 12.3
1/18/14 a 2 1 1 10.8
1/1/14 a 2 3 1 100.3
1/2/14 a 2 3 1 100.3
1/6/14 a 2 3 1 110.4
I can do this for a single column using CROSS APPLY but the syntax for the multiple columns is confusing me. The SQL to generate the temp data is:
DECLARE #test TABLE
(
date DATETIME
,id1 VARCHAR(1)
,id2 INT
,id3 INT
,id4 INT
,value FLOAT
)
INSERT INTO #test VALUES
('2014-01-01','a','1','1','1','1.2')
,('2014-01-02','a','1','1','1',NULL)
,('2014-01-08','a','1','1','1','2.3')
,('2014-01-01','a','2','1','1','10.1')
,('2014-01-02','a','2','1','1','12.3')
,('2014-01-17','a','2','1','1',NULL)
,('2014-01-18','a','2','1','1','10.8')
,('2014-01-01','a','2','3','1','100.3')
,('2014-01-02','a','2','3','1',NULL)
,('2014-01-06','a','2','3','1','110.4')
;
SELECT * FROM #test;
You can use apply for this:
select t.*, coalesce(t.value, tprev.value) as value
from #test t outer apply
(select top 1 value
from #test t2
where t2.id1 = t.id1 and t2.id2 = t.id2 and t2.id3 = t.id3 and t2.id4 = t.id4 and
t2.date < t.date and t2.value is not null
order by t2.date desc
) tprev;