PIVOT over two columns - sql

is there a more elegant way to implement a PIVOT on the following table - basically I am trying to count the typeid's for ever userid, as well as get the first value for each type and pivot the types and the values (using sql server 2012):
This is the expected result:
And here is what I came up with:
SELECT userid ,
MAX([1]) [1] ,
MAX([2]) [2] ,
MAX(value1) [value1] ,
MAX(value2) [value2]
FROM ( SELECT userid ,
typeid ,
FIRST_VALUE(value) OVER ( PARTITION BY userid, typeid ORDER BY testid ) [value] ,
'value' + CONVERT(VARCHAR(20), typeid) [valuepivot]
FROM dbo.test
) t PIVOT( COUNT(typeid) FOR typeid IN ( [1], [2] ) ) as pivottype
PIVOT( MAX(value) FOR [valuepivot] IN ( [value1], [value2] ) ) as pivottype2
GROUP BY userid
Table Schema:
CREATE TABLE [dbo].[Test](
PIVOT( MAX(value) FOR [valuepivot] IN ( [value1], [value2] ) ) as pivottype2
[TestID] [int] PRIMARY KEY IDENTITY(1,1) ,
[UserID] [int] NOT NULL,
[TypeID] [int] NOT NULL,
[Value] [varchar](100) NOT NULL
)
GROUP BY userid

I would just do this using conditional aggregation:
select userid,
sum(case when typeid = 1 then 1 else 0 end) as test1,
sum(case when typeid = 2 then 1 else 0 end) as test2,
max(case when typeid = 1 then value end) as test1,
max(case when typeid = 2 then value end) as test2
from (select t.*,
row_number() over (partition by userid, typeid order by testid) as seqnum
from test
) t
group by userid;
Whether it is simpler or not is a matter of opinion.

Related

Pivot a table in SQL without an aggregate

I have a table with the following format:
ID CODE NAME VALUE
p1 p deflect Yes
a1 d source Prim
p1 p source Dim
I want to pivot to have the following:
ID CODE deflect source
p1 p Yes DIM
a1 d NULL Prim
This is my current code:
SELECT *
from
(
select [ID], [CODE], [NAME], [VALUE]
FROM [DATABASE].[dbo].[TABLE]
) SOURCE_TABLE
pivot
(
max(VALUE)
for [NAME] in ('deflect', 'source')
) PIVOT_TABLE;
But I'm getting:
Incorrect syntax near 'deflect'.
How would you write the pivot code for this?
Why don't you use the conditional aggregation as follows:
select [ID], [CODE],
max(case when [NAME] = 'deflect' then [VALUE] end) as deflect,
max(case when [NAME] = 'source' then [VALUE] end) as source_
FROM [DATABASE].[dbo].[TABLE]
group by [ID], [CODE]

Compare two values from one column based on values in another

I have table where I need to compare two values based on their names in another column to see which one is bigger. Basically:
If **name1** -> value1
If **name2** -> value2
Compare **value1 > value2**
I have done summations but I cannot find a way to use the CASE WHEN approach to this situation.
You can PIVOT the data and have all values in current row. Then, compare them as you like:
SELECT [Id]
,[name1], [name2], [name3], [name4]
FROM [my_table]
PIVOT
(
MAX([value]) FOR [column] IN ([name1], [name2], [name3], [name4])
) PVT
or
SELECT [Id]
,MAX(CASE WHEN [column] = 'name1' THEN [value] END) AS [name1]
,MAX(CASE WHEN [column] = 'name2' THEN [value] END) AS [name2]
,MAX(CASE WHEN [column] = 'name3' THEN [value] END) AS [name3]
,MAX(CASE WHEN [column] = 'name4' THEN [value] END) AS [name4]
FROM [my_table]
GROUP [Id];

Calculate date difference between dates in different rows of a dataset

A table looks like this :
CREATE TABLE [dbo].[HistDT](
[ID] [bigint] NULL,
[StartDtSK] [varchar](8) NULL,
[StartDt] [datetime] NULL,
[status] [nvarchar](30) NULL,
) ON [PRIMARY]
Example data set:
ID | StartDtSK | StartDt | Status |
1 20190520 20-05-2019 12:00:13 10
1 20190520 20-05-2019 10:00:00 5
1 20190414 14-04-2019 13:23:00 2
2 20190312 12-03-2019 10:03:00 10
2 20190308 08-03-2019 18:03:00 1
etc..
I need a query which will display the number of days spent in each status. That would be easy if the table i inherited had an end date. I would then calculate the datediff and pivot for column status values.
Maybe i should create a new table using ssis where i will add an EndDt column which will be the StartDt of the latest added Status.
But is there any way to do this without creating another table?
SQL Server 2008
This is not very pretty, and I haven't tested it for all use cases. I hope you can use it or find inspiration. I'm sure there is a better way :)
declare #table2 table (
[ID] [bigint] NULL,
[StartDtSK] [varchar](8) NULL,
[StartDt] [datetime] NULL,
[status] [nvarchar](30) NULL
)
insert into #table2
values
(1 , '20190520','2019-05-20 12:00:13','10'),
(1 , '20190520','2019-05-20 10:00:00','5'),
(1 , '20190414','2019-04-14 13:23:00','2'),
(2, '20190312', '2019-03-12 10:03:00', '10'),
(2 , '20190308', '2019-03-08 18:03:00', '1')
select *,DATEDIFF(dd,startdt,enddate) as TotalDAys from (
select x.ID,StartDtSK,Startdt,[Status],Enddate from (
select *,ROW_NUMBER() over(partition by id order by startdt) as rn from #table2
) x
cross apply ( select * from (select id,StartDt as Enddate,ROW_NUMBER() over(partition by id order by startdt) as rn2 from #table2 b
)f where (rn +1 = f.rn2 ) and x.id = f.id ) d
union all
select ID,StartDtSK,startdt,[Status],'9999-12-31' as Enddate from (
select *,ROW_NUMBER() over(partition by id order by startdt desc) as rn from #table2
)X where rn=1
)y
order by id,startdt
SQL Server 2008 without cross apply
This might be a bit more pretty :)
select *,DATEDIFF(dd,startdt,enddate) as TotalDAys from (
select x.ID,StartDtSK,Startdt,[Status],case when Enddate is null then '9999-12-31' else Enddate end as Enddate from (
select *,ROW_NUMBER() over(partition by id order by startdt) as rn from #table2
) x
left join (
select * from (select id,StartDt as Enddate,ROW_NUMBER() over(partition by id order by startdt) as rn2 from #table2 b
)f ) d on (rn +1 = d.rn2 ) and x.id = d.id
)y
SQL Server 2012 and above:
Is this what you want?
declare #table2 table (
[ID] [bigint] NULL,
[StartDtSK] [varchar](8) NULL,
[StartDt] [datetime] NULL,
[status] [nvarchar](30) NULL
)
insert into #table2
values
(1 , '20190520','2019-05-20 12:00:13','10'),
(1 , '20190520','2019-05-20 10:00:00','5'),
(1 , '20190414','2019-04-14 13:23:00','2')
select *,Datediff(dd,Startdt,Enddate) as TotalDays from (
select *,LAG(StartDt,1,'9999-12-31') over(partition by ID order by StartDT desc) as EndDate from #table2
)x
Insert a rule that handles current status (9999-12-31) date
Maybe LEAD function is usefull for your question.
Like this
IsNull(DateAdd(SECOND,-1,Cast(LEAD ([StartDt],1) OVER (PARTITION BY [status] ORDER BY [StartDt]) AS DATETIME)),getdate()) AS EndDate

Select row name with max date with grouping

For example:
create table #t
(
[ID] int,
[Date] date,
[Name] varchar(5)
)
insert into #t
values
(1, getdate(),'1-1'),
(2, dateadd(D,-10,getdate()),'2-1'),
(2, dateadd(D,-5,getdate()),'2-2'),
(1, dateadd(M,-1,getdate()),'1-2')
select * from #t
I need to select [Name] for each [ID] with max [Data].
Something like this:
select [1], [2]
from ( select ID, [Date] from #t ) y
pivot (
max(y.[Date])
for y.ID in ([1],[2])
) pvt;
Output:
1 2
2017-04-28 2017-04-23
but instead of [Date] i want to see [Name]
what i want to view
1 2
1-1 2-2
Please help. Thank you.
Please try the following code
create table #t
(
[ID] int,
[Date] date,
[Name] varchar(5)
)
insert into #t
values
(1, getdate(),'1-1'),
(2, dateadd(D,-10,getdate()),'2-1'),
(2, dateadd(D,-5,getdate()),'2-2'),
(1, dateadd(M,-1,getdate()),'1-2')
select [1], [2]
from ( select ID, [Name] from #t ) y
pivot (
max(y.[Name])
for y.ID in ([1],[2])
) pvt;
drop table #t
You can use row_nubmber() with date desc and pivot as below:
;with cte as (
select id, RowN = row_number() over (partition by id order by date desc), name from #t
) select * from
(select id, name from cte where rown = 1 ) s
pivot (max(name) for id in ([1],[2])) p
You can try the following :
SELECT [1], [2]
FROM (SELECT y.ID,
t.Name
FROM (SELECT ID,
MAX([Date]) AS [Date]
FROM #t
GROUP BY ID ) y
INNER JOIN #t t ON y.[Date] = t.[Date]
) x
PIVOT
(
MAX(x.Name)
FOR x.ID IN ([1],[2])
) pvt;
You can see this here -> http://rextester.com/ZGQGSC94965
Hope this helps!!!
try this one.
CREATE TABLE #t
(
[ID] INT ,
[Date] DATE ,
[Name] VARCHAR(5)
)
INSERT INTO #t
VALUES (1, getdate(),'1-1'),
(2, dateadd(D,-10,getdate()),'2-1'),
(2, dateadd(D,-5,getdate()),'2-2'),
(1, dateadd(M,-1,getdate()),'1-2')
SELECT *
FROM #t;
WITH CTE
AS ( SELECT ID ,
MAX(Date) [Date]
FROM #t
GROUP BY ID
),
CTE2
AS ( SELECT cte.ID ,
cte.Date ,
t.name
FROM CTE
OUTER APPLY ( SELECT TOP 1
name
FROM #t
WHERE (ID = cte.ID AND Date = cte.Date)
) T
)
SELECT MAX([1]) [1] ,
MAX([2]) [2]
FROM ( SELECT ID ,
[Date] ,
NAME
FROM CTE2
) y PIVOT ( MAX(y.NAME) FOR y.ID IN ( [1], [2] ) ) pvt
Result
ID Date Name
----------- ---------- -----
1 2017-05-02 1-1
2 2017-04-22 2-1
2 2017-04-27 2-2
1 2017-04-02 1-2
(4 row(s) affected)
1 2
----- -----
1-1 2-2
(1 row(s) affected)

How can I display repeated values only one time and have '-' if it repeats

I have some subqueries that retreives the same values for each PolicyNumber. How can I substitute repeated value with '-' and only display it one in a top row for each policy?
Right now I have this:
But I need something like this:
SELECT
-------------/* GrossPremium*/
(SELECT ISNULL(SUM(tblFin_InvoiceDetails.AmtBilled), 0)
FROM tblFin_InvoiceDetails WITH (NOLOCK)
WHERE (tblFin_InvoiceDetails.ChargeType = 'P')
AND (tblFin_InvoiceDetails.InvoiceNum = INV.InvoiceNum))
AS GrossPremium
--------------/*CompanyCommissionPercentage*/
,((SELECT ISNULL(SUM(tblFin_InvoiceDetails.MGAAmt), 0)
FROM tblFin_InvoiceDetails
WHERE (tblFin_InvoiceDetails.ChargeType = 'P')
AND (tblFin_InvoiceDetails.InvoiceNum = INV.InvoiceNum))
+
CASE WHEN INV.Remitter = 'B' then
(SELECT ISNULL(SUM(tblFin_InvoiceDetails.RemitterAmt), 0)
FROM tblFin_InvoiceDetails
WHERE (tblFin_InvoiceDetails.ChargeType = 'P')
AND (tblFin_InvoiceDetails.InvoiceNum = INV.InvoiceNum))----------------RemitterCommission
ELSE
(SELECT ISNULL(SUM(tblFin_InvoicedItemsPayees.PayeeAmt), 0)
FROM tblFin_InvoicedItemsPayees
INNER JOIN tblFin_PolicyCharges pc on pc.ChargeCode = tblFin_InvoicedItemsPayees.ChargeCode and pc.chargeType = 'P'
WHERE (tblFin_InvoicedItemsPayees.InvoiceNum = INV.InvoiceNum and tblFin_InvoicedItemsPayees.PayeeGuid = INV.ProducerLocationGuid))
END) * 100 /
NULLIF((SELECT ISNULL(SUM(tblFin_InvoiceDetails.AmtBilled), 0)
FROM tblFin_InvoiceDetails WITH (NOLOCK)
WHERE (tblFin_InvoiceDetails.ChargeType = 'P')
AND (tblFin_InvoiceDetails.InvoiceNum = INV.InvoiceNum)),0)
AS CompanyCommissionPercentage
FROM [tblFin_PayablesWorking] PW
INNER JOIN tblFin_Invoices INV ON PW.InvoiceNumber=INV.InvoiceNum
Well since you do not mention you full query and Table schema, i will give your answer with two simple example.If you want replace your repeated value with - the follow this query(please change the columns name according to your needs).
IF YOU WANT TO SELECT YOUR EXISTING TABLE:
;with ts as (
select S1.[ProductID], row_number() over (partition by S1.[ProductID] order by S1.[ProductID]) as seqnum
from (SELECT [SalesID],[ProductID] FROM [Sales]) AS S1 --Replace 'SELECT [ProductID] FROM [Sales]' with your Subquery and change the column accordingly
)
SELECT
(case when seqnum = 1 then [ProductID] ELSE '-' end) as [ProductID]
FROM ts
FOR USING SUBQUERY:
--CREATE TABLE [dbo].[Sales](
-- [SalesID] [uniqueidentifier] NOT NULL DEFAULT (newid()),
-- [ProductID] [int] NOT NULL,
-- [EmployeeID] [int] NOT NULL,
-- [Quantity] [smallint] NOT NULL,
-- [SaleDate] [datetime] NOT NULL CONSTRAINT [DF_SaleDate] DEFAULT (getdate()),
-- CONSTRAINT [PK_SalesID] PRIMARY KEY CLUSTERED
--(
-- [SalesID] ASC
--)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
--) ON [PRIMARY]
--GO
--INSERT [dbo].[Sales] ([SalesID], [ProductID], [EmployeeID], [Quantity], [SaleDate]) VALUES (N'9498d566-e31b-4ac8-ab54-1c898471fba8', 2, 1, 1, CAST(N'2012-03-01 00:00:00.000' AS DateTime))
--INSERT [dbo].[Sales] ([SalesID], [ProductID], [EmployeeID], [Quantity], [SaleDate]) VALUES (N'69c7dff4-fbac-48d3-ae0a-5027c816acd2', 2, 2, 2, CAST(N'2012-04-01 00:00:00.000' AS DateTime))
--INSERT [dbo].[Sales] ([SalesID], [ProductID], [EmployeeID], [Quantity], [SaleDate]) VALUES (N'a40b9505-4a2c-4186-a89b-88a401248a58', 1, 1, 4, CAST(N'2012-02-01 00:00:00.000' AS DateTime))
--INSERT [dbo].[Sales] ([SalesID], [ProductID], [EmployeeID], [Quantity], [SaleDate]) VALUES (N'04856027-d7ad-40fe-889b-8d933595ffde', 3, 1, 2, CAST(N'2012-02-01 00:00:00.000' AS DateTime))
--INSERT [dbo].[Sales] ([SalesID], [ProductID], [EmployeeID], [Quantity], [SaleDate]) VALUES (N'173be2de-3b80-4a3d-8bcc-a74d0d70b3a9', 3, 2, 1, CAST(N'2012-03-01 00:00:00.000' AS DateTime))
--GO
;with ts as (
SELECT
JOIN1.[SalesID] AS [SalesID]
, JOIN1.[ProductID]
, JOIN1.seqnum AS seqnum
, JOIN2.[EmployeeID], JOIN2.seqnum2 AS seqnum2
FROM
(
select row_number() over (order by S1.[SalesID] asc) as RowNumber
, S1.[SalesID] AS [SalesID]
, S1.[ProductID] AS [ProductID]
, row_number() over (partition by S1.[ProductID] order by S1.[SalesID]) as seqnum
from (SELECT [SalesID],[ProductID] FROM [Sales]) AS S1 --Replace 'SELECT [ProductID] FROM [Sales]' with your Subquery ( For Example GrossPremium) and change the column accordingly. Remember you need some thing common for Iner join, in this case [SalesID]
)AS JOIN1
INNER JOIN
(
select row_number() over (order by S2.[SalesID] asc) as RowNumber
, S2.[SalesID] AS [SalesID]
, S2.[EmployeeID] AS [EmployeeID]
, row_number() over (partition by S2.[EmployeeID] order by S2.[SalesID]) as seqnum2
from (SELECT [SalesID],[EmployeeID] FROM [Sales]) AS S2 --Replace 'SELECT [[SalesID]] FROM [Sales]' with your Subquery ( For Example CompanyCommissionPercentage) and change the column accordingly. Remember you need some thing common for Iner join, in this case [SalesID]
)AS JOIN2
ON JOIN1.[SalesID]=JOIN2.[SalesID]
)
SELECT
(case when seqnum = 1 then [ProductID] ELSE '-' end) as [ProductID]
,(case when seqnum2 = 1 then [EmployeeID] ELSE '-' end) as [EmployeeID]
FROM (Select TOP 10000000 *FROM ts ORDER BY [SalesID] ASC ) AS ts -- Mentioning TOP is Must, or it will give Error
I do know why you are using - instead of NULL, - will take space
if you wanted to do it in SQL (you probably shouldn't it's pretty ugly) you could do something like this using LAG(). It relies on having a field that you can use to sort the records for each Policy Number, in my dummy data below I included a field called RecordID to do this.
SELECT
PolicyNumber
,CASE
WHEN LAG(GrossPremium) OVER(PARTITION BY PolicyNumber ORDER BY RecordID) IS NULL
THEN CAST(GrossPremium AS VARCHAR(MAX))
ELSE '-'
END GrossPremium
,CASE
WHEN LAG(CompanyComissionPercentage) OVER(PARTITION BY PolicyNumber ORDER BY RecordID) IS NULL
THEN CAST(CompanyComissionPercentage AS VARCHAR(MAX))
ELSE '-'
END CompanyComissionPercentage
,CASE
WHEN LAG(RemitterCommissionPercentage) OVER(PARTITION BY PolicyNumber ORDER BY RecordID) IS NULL
THEN CAST(RemitterCommissionPercentage AS VARCHAR(MAX))
ELSE '-'
END RemitterCommissionPercentage
,CASE
WHEN LAG(RemitterCommission) OVER(PARTITION BY PolicyNumber ORDER BY RecordID) IS NULL
THEN CAST(RemitterCommission AS VARCHAR(MAX))
ELSE '-'
END GrossCommission
,CASE
WHEN LAG(RemitterCommission) OVER(PARTITION BY PolicyNumber ORDER BY RecordID) IS NULL
THEN CAST(RemitterCommission AS VARCHAR(MAX))
ELSE '-'
END GrossCommission
FROM
(
-- Dummy data
SELECT
1234 PolicyNumber -- Partition the LAG() on the policy number.
,1 RecordID -- use this to order the LAG() function.
,8749.00 GrossPremium
,18 CompanyComissionPercentage
,10 RemitterCommissionPercentage
,874.90 RemitterCommission
,1574.82 GrossCommission
UNION ALL
SELECT
1234
,2 RecordID
,8749.00
,18
,10
,874.90
,1574.82
UNION ALL
SELECT
5678
,1 RecordID
,8749.00
,18
,10
,874.90
,1574.82
) x;