How to turn data with pivot? - sql-server

Data:
CASEID
VISITDATE
QUESTION
ANSWER
COMEFROM
1
2021-01-02
Q1
1
H
1
2021-01-02
Q2
2
O
1
2021-01-02
Q3
3
B
1
2021-01-08
Q1
4
H
1
2021-01-08
Q2
5
O
1
2021-01-08
Q3
6
B
Expected result:
CASEID
VISITDATE
Q1
Q2
Q3
1
2021-01-02
1
2
3
1
2021-01-08
4
5
6
My code:
SELECT CaseID, Visitdate, [Q1], [Q2], [Q3]
from
(
select CaseID, Visitdate, Question, ANSWER, COMEFROM
from DATA
) as v
pivot
(
MAX(ANSWER)
FOR Question IN ([Q1], [Q2], [Q3])
) as p
OUTPUT:
CASEID
VISITDATE
Q1
Q2
Q3
1
2021-01-02
1
null
null
1
2021-01-02
null
2
null
1
2021-01-02
null
null
3
1
2021-01-08
4
null
null
1
2021-01-08
null
5
null
1
2021-01-08
null
null
6

You can use below PIVOT code. When you do pivot , the columns not in the pivot are by default applied grouping.
declare #table table(CaseId int, VisitDate date, Question char(2), Answer int)
insert into #table values
(1 ,'2021-01-02','Q1',1)
,(1 ,'2021-01-02','Q2',2)
,(1 ,'2021-01-02','Q3',3)
,(1 ,'2021-01-08','Q1',4)
,(1 ,'2021-01-08','Q2',5)
,(1 ,'2021-01-08','Q3',6)
SELECT * FROM #table
PIVOT
(
MAX(Answer) FOR Question in ([Q1],[Q2],[Q3])
) as pvt
CaseId
VisitDate
Q1
Q2
Q3
1
2021-01-02
1
2
3
1
2021-01-08
4
5
6

Related

Dynamically find unpaid invoices

How can I dynamically find un-paid invoices from tables bellow:
Invoices Table
InvoiceID, Date CustomerID, Amount
1 06/01/2022 1 5000.00
2 08/03/2022 1 4000.00
3 08/25/2022 1 3000.00
4 09/05/2022 1 4500.00
5 09/25/2022 1 4500.00
6 010/10/2022 1 2000.00
7 11/20/2022 1 2500.00
Payments Table:-
PaymentID Date CustomerID Amount
1 06/10/2022 1 3000.00
2 06/25/2022 1 4000.00
3 07/15/2022 1 2000.00
4 09/10/2022 1 3000.00
5 10/22/2022 1 4000.00
6 10/24/2022 1 1500.00
7 10/28/2022 1 1000.00
8 11/14/2022 1 500.00
Try to start with this:
SELECT I.CustomerID
, I.AmountTotal-ISNULL(P.AmountTotal,0) as AmountDiff
FROM
( SELECT CustomerID
, SUM(Amount) AmountTotal
FROM <invoices_table>
GROUP
BY CustomerID
) I
LEFT
OUTER
JOIN
( SELECT CustomerID
, SUM(Amount) AmountTotal
FROM <payments_table>
GROUP
BY CustomerID
) P
ON I.CustomerID = P.CustomerID
WHERE I.AmountTotal <= P.AmountTotal

How to count number of months in T-SQL

I've got a problem in SQL Server.
"Whate'er is well conceived is clearly said, And the words to say it flow with ease", Nicolas Boileau-Despreaux
Well, I don't think I'll be able to make it clear but I'll try ! And I'd like to apologize for my bad english !
I've got this table :
id ind lvl result date
1 1 a 3 2017-01-31
2 1 a 3 2017-02-28
3 1 a 1 2017-03-31
4 1 a 1 2017-04-30
5 1 a 1 2017-05-31
6 1 b 1 2017-01-31
7 1 b 3 2017-02-28
8 1 b 3 2017-03-31
9 1 b 1 2017-04-30
10 1 b 1 2017-05-31
11 2 a 3 2017-01-31
12 2 a 1 2017-02-28
13 2 a 3 2017-03-31
14 2 a 1 2017-04-30
15 2 a 3 2017-05-31
I'd like to count the number of month the combo {ind, lvl} remain in the result 1 before re-initializing the number of month to 0 if the result is not 1.
Clearly, I need to get something like that :
id ind lvl result date BadResultRemainsFor%Months
1 1 a 3 2017-01-31 0
2 1 a 3 2017-02-28 0
3 1 a 1 2017-03-31 1
4 1 a 1 2017-04-30 2
5 1 a 1 2017-05-31 3
6 1 b 1 2017-01-31 1
7 1 b 3 2017-02-28 0
8 1 b 3 2017-03-31 0
9 1 b 1 2017-04-30 1
10 1 b 1 2017-05-31 2
11 2 a 3 2017-01-31 0
12 2 a 1 2017-02-28 1
13 2 a 3 2017-03-31 0
14 2 a 1 2017-04-30 1
15 2 a 3 2017-05-31 0
So that if I was looking for the number of months the result was 1 for the date 2017-05-31 with the id 1 and the lvl a, I know it's been 3 months.
Assume all the date the the end day of month:
;WITH tb(id,ind,lvl,result,date) AS(
select 1,1,'a',3,'2017-01-31' UNION
select 2,1,'a',3,'2017-02-28' UNION
select 3,1,'a',1,'2017-03-31' UNION
select 4,1,'a',1,'2017-04-30' UNION
select 5,1,'a',1,'2017-05-31' UNION
select 6,1,'b',1,'2017-01-31' UNION
select 7,1,'b',3,'2017-02-28' UNION
select 8,1,'b',3,'2017-03-31' UNION
select 9,1,'b',1,'2017-04-30' UNION
select 10,1,'b',1,'2017-05-31' UNION
select 11,2,'a',3,'2017-01-31' UNION
select 12,2,'a',1,'2017-02-28' UNION
select 13,2,'a',3,'2017-03-31' UNION
select 14,2,'a',1,'2017-04-30' UNION
select 15,2,'a',3,'2017-05-31'
)
SELECT t.id,t.ind,t.lvl,t.result,t.date
,CASE WHEN t.isMatched=1 THEN ROW_NUMBER()OVER(PARTITION BY t.ind,t.lvl,t.id-t.rn ORDER BY t.id) ELSE 0 END
FROM (
SELECT t1.*,c.MonthDiff,CASE WHEN c.MonthDiff=t1.result THEN 1 ELSE 0 END AS isMatched
,CASE WHEN c.MonthDiff=t1.result THEN ROW_NUMBER()OVER(PARTITION BY t1.ind,t1.lvl,CASE WHEN c.MonthDiff=t1.result THEN 1 ELSE 0 END ORDER BY t1.id) ELSE null END AS rn
FROM tb AS t1
LEFT JOIN tb AS t2 ON t1.ind=t2.ind AND t1.lvl=t2.lvl AND t2.id=t1.id-1
CROSS APPLY(VALUES(ISNULL(DATEDIFF(MONTH,t2.date,t1.date),1))) c(MonthDiff)
) AS t
ORDER BY t.id
id ind lvl result date
----------- ----------- ---- ----------- ---------- --------------------
1 1 a 3 2017-01-31 0
2 1 a 3 2017-02-28 0
3 1 a 1 2017-03-31 1
4 1 a 1 2017-04-30 2
5 1 a 1 2017-05-31 3
6 1 b 1 2017-01-31 1
7 1 b 3 2017-02-28 0
8 1 b 3 2017-03-31 0
9 1 b 1 2017-04-30 1
10 1 b 1 2017-05-31 2
11 2 a 3 2017-01-31 0
12 2 a 1 2017-02-28 1
13 2 a 3 2017-03-31 0
14 2 a 1 2017-04-30 1
15 2 a 3 2017-05-31 0
By slightly tweaking your input data and slightly tweaking how we define the requirement, it becomes quite simple to produce the expected results.
First, we tweak your date values so that the only thing that varies is the month and year - the days are all the same. I've chosen to do that my adding 1 day to each value1. The fact that this produces results which are one month advanced doesn't matter here, since all values are similarly transformed, and so the monthly relationships stay the same.
Then, we introduce a numbers table - here, I've assumed a small fixed table is adequate. If it doesn't fit your needs, you can easily locate examples online for creating a large fixed numbers table that you can use for this query.
And, finally, we recast the problem statement. Instead of trying to count months, we instead ask "what's the smallest number of months, greater of equal to zero, that I need to go back from the current row, to locate a row with a non-1 result?". And so, we produce this query:
declare #t table (id int not null,ind int not null,lvl varchar(13) not null,
result int not null,date date not null)
insert into #t(id,ind,lvl,result,date) values
(1 ,1,'a',3,'20170131'), (2 ,1,'a',3,'20170228'), (3 ,1,'a',1,'20170331'),
(4 ,1,'a',1,'20170430'), (5 ,1,'a',1,'20170531'), (6 ,1,'b',1,'20170131'),
(7 ,1,'b',3,'20170228'), (8 ,1,'b',3,'20170331'), (9 ,1,'b',1,'20170430'),
(10,1,'b',1,'20170531'), (11,2,'a',3,'20170131'), (12,2,'a',1,'20170228'),
(13,2,'a',3,'20170331'), (14,2,'a',1,'20170430'), (15,2,'a',3,'20170531')
;With Tweaked as (
select
*,
DATEADD(day,1,date) as dp1d
from
#t
), Numbers(n) as (
select 0 union all select 1 union all select 2 union all select 3 union all select 4
union all
select 5 union all select 6 union all select 7 union all select 8 union all select 9
)
select
id, ind, lvl, result, date,
COALESCE(
(select MIN(n) from Numbers n1
inner join Tweaked t2
on
t2.ind = t1.ind and
t2.lvl = t1.lvl and
t2.dp1d = DATEADD(month,-n,t1.dp1d)
where
t2.result != 1
),
1) as [BadResultRemainsFor%Months]
from
Tweaked t1
The COALESCE is just there to deal with the edge case, such as for your 1,b data, where there is no previous row with a non-1 result.
Results:
id ind lvl result date BadResultRemainsFor%Months
----------- ----------- ------------- ----------- ---------- --------------------------
1 1 a 3 2017-01-31 0
2 1 a 3 2017-02-28 0
3 1 a 1 2017-03-31 1
4 1 a 1 2017-04-30 2
5 1 a 1 2017-05-31 3
6 1 b 1 2017-01-31 1
7 1 b 3 2017-02-28 0
8 1 b 3 2017-03-31 0
9 1 b 1 2017-04-30 1
10 1 b 1 2017-05-31 2
11 2 a 3 2017-01-31 0
12 2 a 1 2017-02-28 1
13 2 a 3 2017-03-31 0
14 2 a 1 2017-04-30 1
15 2 a 3 2017-05-31 0
1An alternative way to perform the adjustment is to use a DATEADD/DATEDIFF pair to perform a "floor" operation against the dates:
DATEADD(month,DATEDIFF(month,0,date),0) as dp1d
Which resets all of the date values to be the first of their own month rather than the following month. This may fell more "natural" to you, or you may already have such values available in your original data.
Assuming the dates are continously increasing in month, you can use window function like so:
select
t.id, ind, lvl, result, dat,
case when result = 1 then row_number() over (partition by grp order by id) else 0 end x
from (
select t.*,
dense_rank() over (order by e, result) grp
from (
select
t.*,
row_number() over (order by id) - row_number() over (partition by ind, lvl, result order by id) e
from your_table t
order by id) t ) t;

MSSQL 2008 Merge Contiguous Dates With Groupings

I have searched high and low for weeks now trying to find a solution to my problem.
As far as I can ascertain, my SQL Server version (2008r2) is a limiting factor on this but, I am positive there is a solution out there.
My problem is as follows:
A have a table with potential contiguous dates in the form of Customer-Status-DateStart-DateEnd-EventID.
I need to merge contiguous dates by customer and status - the status field can shift up and down throughout a customers pathway.
Some example data is as follows:
DECLARE #Tbl TABLE([CustomerID] INT
,[Status] INT
,[DateStart] DATE
,[DateEnd] DATE
,[EventID] INT)
INSERT INTO #Tbl
VALUES (1,1,'20160101','20160104',1)
,(1,1,'20160104','20160108',3)
,(1,2,'20160108','20160110',4)
,(1,1,'20160110','20160113',7)
,(1,3,'20160113','20160113',9)
,(1,3,'20160113',NULL,10)
,(2,1,'20160101',NULL,2)
,(3,2,'20160109','20160110',5)
,(3,1,'20160110','20160112',6)
,(3,1,'20160112','20160114',8)
Desired output:
Customer | Status | DateStart | DateEnd
---------+--------+-----------+-----------
1 | 1 | 2016-01-01| 2016-01-08
1 | 2 | 2016-01-08| 2016-01-10
1 | 1 | 2016-01-10| 2016-01-13
1 | 3 | 2016-01-13| NULL
2 | 1 | 2016-01-01| NULL
3 | 2 | 2016-01-09| 2016-01-10
3 | 1 | 2016-01-10| 2016-01-14
Any ideas / code will be greatly received.
Thanks,
Dan
Try this
DECLARE #Tbl TABLE([CusomerID] INT
,[Status] INT
,[DateStart] DATE
,[DateEnd] DATE
,[EventID] INT)
INSERT INTO #Tbl
VALUES (1,1,'20160101','20160104',1)
,(1,1,'20160104','20160108',3)
,(1,2,'20160108','20160110',4)
,(1,1,'20160110','20160113',7)
,(1,3,'20160113','20160113',9)
,(1,3,'20160113',NULL,10)
,(2,1,'20160101',NULL,2)
,(3,2,'20160109','20160110',5)
,(3,1,'20160110','20160112',6)
,(3,1,'20160112','20160114',8)
;WITH CTE
AS
(
SELECT CusomerID ,
Status ,
DateStart ,
COALESCE(DateEnd, '9999-01-01') AS DateEnd,
EventID,
ROW_NUMBER() OVER (ORDER BY CusomerID, EventID) RowId,
ROW_NUMBER() OVER (PARTITION BY CusomerID, Status ORDER BY EventID) StatusRowId FROM #Tbl
)
SELECT
A.CusomerID ,
A.Status ,
A.DateStart ,
CASE WHEN A.DateEnd = '9999-01-01' THEN NULL
ELSE A.DateEnd END AS DateEnd
FROM
(
SELECT
CTE.CusomerID,
CTE.Status,
MIN(CTE.DateStart) AS DateStart,
MAX(CTE.DateEnd) AS DateEnd
FROM
CTE
GROUP BY
CTE.CusomerID,
CTE.Status,
CTE.StatusRowId -CTE.RowId
) A
ORDER BY A.CusomerID, A.DateStart
Output
CusomerID Status DateStart DateEnd
----------- ----------- ---------- ----------
1 1 2016-01-01 2016-01-08
1 2 2016-01-08 2016-01-10
1 1 2016-01-10 2016-01-13
1 3 2016-01-13 NULL
2 1 2016-01-01 NULL
3 2 2016-01-09 2016-01-10
3 1 2016-01-10 2016-01-14

Need select 2 rows from Table2, which is joined with Table1. See description

For example i have a Table1:
ID Specified TIN Value DateCreated
----------------------------------
1 0 tin1 45 2014-12-30
2 1 tin2 34 2013-01-05
3 0 tin3 23 2015-02-20
4 3 tin4 47 2013-06-04
5 3 tin5 12 2012-04-02
And a Table2:
ID Table1ID RegistrationDate
----------------------------------
1 1 2015-10-12
2 2 2015-07-21
3 1 2015-11-26
4 1 2015-12-04
5 2 2015-09-18
I need select all columns from Table1 with first and last RegistrationDate column in Table2. The answer should be
ID Specified TIN Value DateCreated FirstRegDate LastRegDate
---------------------------------------------------------------
1 0 tin1 45 2014-12-30 2015-10-12 2015-12-04
2 1 tin2 34 2013-01-05 2015-07-21 2015-09-18
3 0 tin3 23 2015-02-20 NULL NULL
4 3 tin4 47 2013-06-04 NULL NULL
5 3 tin5 12 2012-04-02 NULL NULL
Hi one possible solution can be something similar to pseudo query below(if you can prepare the tables I will modify to reflect actual query)
SELECT table1.*, inlineTable2.firstRegDate, inlineTable2.lastRegDate
FROM Table1
LEFT JOIN
(
SELECT
Table1ID AS id,
MIN(registrationDate) as firstRegDate,
MAX(regsitrationDate) as lastRegDate
FROM table2
GROUP BY table1ID
) AS inlineTable2
ON table1.id = inlineTable2.id
You can group by all columns in table1, and look up the minumum and maximum registration date for the group:
select ID
, Specified
, ... other columns from table1 ...
, min(RegistrationDate)
, max(RegistrationDate)
from Table1 t1
left join
Table2 t2
on t1.ID = t2.Table1ID
group by
ID
, Specified
, ... other columns from table1 ...

Getting latest value from joined table

I have 2 tables joined by a mapping table
I want to get the the intensity from the last Tap.
With IssuesCTE AS
(
Select * from Issues
),
TapsCTE AS
(
SELECT
MapIssueTaps.IssueId,
SD.Id,
SD.Intensity,
SD.Notes,
Row_Number() OVER
(
PARTITION BY MapIssueTaps.IssueId ORDER BY SD.CreatedOn DESC) AS RowNumber
FROM Taps SD
join MapIssueTaps on MapIssueTaps.TapId=SD.Id
)
Select IssuesCTE.*,TapsCTE.* from IssuesCTE,TapsCTE
join MapIssueTaps on MapIssueTaps.TapId = TapsCTE.Id
--where RowNumber=1
Here is all the data:
UserId Id Name Intensity CreatedOn UpdatedOn Description IssueId Id Intensity Notes RowNumber
A291B6F4-866D-4EFA-9522-7AA45710C7E0 1 c 2 2014-02-02 03:53:33.200 2014-02-02 03:53:33.200 cx 1 3 8 dd 1
A291B6F4-866D-4EFA-9522-7AA45710C7E0 8 d 5 2014-02-03 22:16:24.157 2014-02-03 22:16:24.157 d 1 3 8 dd 1
A291B6F4-866D-4EFA-9522-7AA45710C7E0 1 c 2 2014-02-02 03:53:33.200 2014-02-02 03:53:33.200 cx 1 2 1 xzc 2
A291B6F4-866D-4EFA-9522-7AA45710C7E0 8 d 5 2014-02-03 22:16:24.157 2014-02-03 22:16:24.157 d 1 2 1 xzc 2
A291B6F4-866D-4EFA-9522-7AA45710C7E0 1 c 2 2014-02-02 03:53:33.200 2014-02-02 03:53:33.200 cx 1 1 8 ss 3
A291B6F4-866D-4EFA-9522-7AA45710C7E0 8 d 5 2014-02-03 22:16:24.157 2014-02-03 22:16:24.157 d 1 1 8 ss 3
I would like this:
UserId Id Name Intensity CreatedOn UpdatedOn Description IssueId Id Intensity Notes RowNumber
A291B6F4-866D-4EFA-9522-7AA45710C7E0 1 c 2 2014-02-02 03:53:33.200 2014-02-02 03:53:33.200 cx 1 3 8 dd 1
A291B6F4-866D-4EFA-9522-7AA45710C7E0 8 d 5 2014-02-03 22:16:24.157 2014-02-03 22:16:24.157 d 1 3 null dd 1
How can I do it?

Resources