Merging queries - sql

I have two queries that work great but I need to merge the two results and have so far been unable to. It is almost as if I need to pivot the secondary results but it goes way above my knowledge.
The first query gathers all the details (many columns have been omitted for brevity). The second grabs alternate bill codes to be filled into any NULL or EMPTY MisCptID fields if an alternate code is available.
So if MisCptID_01 and MisCptID_02 have data they are left as is but then 3 - 6 need to use the available list of CPTs from the alternate ones. If there are no alternate ones then they stay NULL or EMPTY.
Results from first query (Sample):
ID Location MisCptID_01 MisCptID_02 MisCptID_03 MisCptID_04 MisCptID_05 MisCptID_06
AXXXXXXXX9443 OXXXXXOR 43248 43239 NULL NULL NULL NULL
AXXXXXXXX1163 EXXXXXED 43249 43247 43239 NULL NULL NULL
AXXXXXXXX1765 OXXXXXOR 43251 45385 45382 45384 45380 45381
Results from second Query containing alternate codes (Sample):
ID AlternateCodeCode StandardAmount
AXXXXXXXX9443 80048 49.00
AXXXXXXXX9443 81001 38.00
AXXXXXXXX9443 76000 0.00
AXXXXXXXX1163 84703 80.50
AXXXXXXXX1163 82040 21.70
AXXXXXXXX1163 83036 49.00
AXXXXXXXX1163 85014 10.90
AXXXXXXXX1163 85018 10.90
AXXXXXXXX1163 86901 18.10
AXXXXXXXX1765 88305 185.00
AXXXXXXXX1765 88311 32.00
AXXXXXXXX1765 93005 125.00
AXXXXXXXX1765 80048 49.00
AXXXXXXXX1765 85027 10.80
Desired Results for merging the query results:
ID Location MisCptID_01 MisCptID_02 MisCptID_03 MisCptID_04 MisCptID_05 MisCptID_06
AXXXXXXXX9443 OXXXXXOR 43248 43239 80048 81001 76000 NULL
AXXXXXXXX1163 EXXXXXED 43249 43247 43239 84703 83036 86901
AXXXXXXXX1765 OXXXXXOR 43251 45385 45382 45384 45380 45381

You can unpivot the first query, union the results with the second query and then pivot them back again, like this:
-- sample data:
DECLARE #ResultsOfFirstQuery TABLE (
ID VARCHAR(20) NOT NULL,
Location VARCHAR(10) NOT NULL,
MisCptID_01 INT NULL,
MisCptID_02 INT NULL,
MisCptID_03 INT NULL,
MisCptID_04 INT NULL,
MisCptID_05 INT NULL,
MisCptID_06 INT NULL
)
INSERT INTO #ResultsOfFirstQuery (ID, Location, MisCptID_01, MisCptID_02, MisCptID_03, MisCptID_04, MisCptID_05, MisCptID_06) VALUES
('AXXXXXXXX9443','OXXXXXOR',43248,43239,NULL ,NULL ,NULL ,NULL ),
('AXXXXXXXX1163','EXXXXXED',43249,43247,43239,NULL ,NULL ,NULL ),
('AXXXXXXXX1765','OXXXXXOR',43251,45385,45382,45384,45380,45381)
DECLARE #ResultsOfSecondQuery TABLE (
ID VARCHAR(20) NOT NULL,
AlternateCodeCode INT NOT NULL,
StandardAmount NUMERIC(10,2) NOT NULL
)
INSERT INTO #ResultsOfSecondQuery (ID, AlternateCodeCode, StandardAmount) VALUES
('AXXXXXXXX9443',80048,49.00 ),
('AXXXXXXXX9443',81001,38.00 ),
('AXXXXXXXX9443',76000,0.00 ),
('AXXXXXXXX1163',84703,80.50 ),
('AXXXXXXXX1163',82040,21.70 ),
('AXXXXXXXX1163',83036,49.00 ),
('AXXXXXXXX1163',85014,10.90 ),
('AXXXXXXXX1163',85018,10.90 ),
('AXXXXXXXX1163',86901,18.10 ),
('AXXXXXXXX1765',88305,185.00),
('AXXXXXXXX1765',88311,32.00 ),
('AXXXXXXXX1765',93005,125.00),
('AXXXXXXXX1765',80048,49.00 ),
('AXXXXXXXX1765',85027,10.80 )
-- query:
SELECT p.ID, q1.Location, p.[1] AS MisCptID_01, p.[2] AS MisCptID_02, p.[3] AS MisCptID_03, p.[4] AS MisCptID_04, p.[5] AS MisCptID_05, p.[6] AS MisCptID_06
FROM (
SELECT x.ID, x.Code, ROW_NUMBER() OVER (PARTITION BY x.ID ORDER BY x.SourceQuery, x.Position) AS Position
FROM (
SELECT u.ID, u.Code, 1 AS SourceQuery, RIGHT(u.ColumnName,2) AS Position
FROM (
SELECT ID, MisCptID_01, MisCptID_02, MisCptID_03, MisCptID_04, MisCptID_05, MisCptID_06
FROM #ResultsOfFirstQuery
) q
UNPIVOT (Code FOR ColumnName IN (MisCptID_01, MisCptID_02, MisCptID_03, MisCptID_04, MisCptID_05, MisCptID_06)) u
UNION ALL
SELECT ID, AlternateCodeCode, 2 AS SourceQuery, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ID) AS Position
FROM #ResultsOfSecondQuery
) x
) y
PIVOT (MAX(Code) FOR Position IN ([1],[2],[3],[4],[5],[6])) p
INNER JOIN #ResultsOfFirstQuery q1 ON q1.ID = p.ID

Related

Recursively Conditionally get parent Id

I have two tables: Budget Line and Expense. They are structured in such a way that an expense must have either a parent record in the budget line table, or a parent record in the expense table. I need to select all child-most expenses for each budget line.
For example - BudgetLine:
Id
Description
1
TEST123
2
OTHERTEST
Expense:
Id
ParentId
ParentType
Description
1
1
BudgetLine
Group of Expenses
2
1
Expense
Expense # 1
3
1
Expense
Expense # 2
4
2
BudgetLine
Expense 3
Desired result:
BudgetLineId
ExpenseId
Description
1
2
Expense # 1
1
3
Expense # 2
2
4
Expense # 3
I am looking to omit expenses in the result only if they are the only sub-child. Note that an expense may have many children, grandchildren, etc.
I have tried the following, and researching various recursive CTE methods:
WITH RCTE AS
(
SELECT Expense.Id, Expense.ParentId, Expense.ParentType, 1 AS Lvl, Expense.Id as startId FROM Expense
UNION ALL
SELECT rh.Id, rh.ParentId, rh.ParentType, Lvl+1 AS Lvl, rc.Id as startId FROM dbo.Expense rh
INNER JOIN RCTE rc ON rh.Id = rc.ParentId and rc.ParentType = 'Expense'
),
FilteredRCTE AS
(
SELECT startId, MAX(LVL) AS Lvl
FROM RCTE
GROUP BY startID
),
RecursiveData AS
(
SELECT FilteredRCTE.startId AS ExpenseId, RCTE.ParentId AS BudgetLineId
FROM FilteredRCTE
JOIN RCTE ON FilteredRCTE.startId = RCTE.startId AND FilteredRCTE.Lvl = RCTE.Lvl
)
SELECT *
FROM RecursiveData
Which did in-fact obtain all the child Expenses and their associated parent BudgetLine, but it also included the middle-tier expenses (such as item 1 in the example) and I cannot figure out how to filter those middle-tier items out.
Here is a script to create tables / insert sample data:
CREATE TABLE [dbo].[BudgetLine]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[Description] [varchar](500) NULL,
) ON [PRIMARY]
GO
INSERT INTO dbo.BudgetLine VALUES ('TEST123')
INSERT INTO dbo.BudgetLine VALUES ('OTHERTEST')
CREATE TABLE [dbo].[Expense]
(
[Id] [int] IDENTITY(1,1) NOT NULL,
[ParentId] [int] NOT NULL,
[ParentType] [varchar](100) NOT NULL,
[Description] [varchar](max) NULL,
) ON [PRIMARY]
GO
INSERT INTO dbo.Expense VALUES ('1', 'BudgetLine', 'Group of Expenses')
INSERT INTO dbo.Expense VALUES ('1', 'Expense', 'Expense # 1')
INSERT INTO dbo.Expense VALUES ('1', 'Expense', 'Expense # 2')
INSERT INTO dbo.Expense VALUES ('2', 'BudgetLine', 'Expense # 3')
Maybe I have oversimplified, but the following returns your desired results, by checking that there is no other expense row connected to the current row.
WITH RCTE AS
(
SELECT E.Id ExpenseId, E.ParentId, E.ParentType
FROM #Expense E
UNION ALL
SELECT RH.Id, RH.ParentId, RH.ParentType
FROM #Expense RH
INNER JOIN RCTE RC ON RH.Id = RC.ParentId AND RC.ParentType = 'Expense'
)
SELECT *
FROM RCTE R1
WHERE NOT EXISTS (
SELECT 1
FROM RCTE R2
WHERE R2.ParentId = R1.ExpenseId AND R2.ParentType = 'Expense'
);

Select rows after the current row which are null and combine them to rows

I have written the following SQL code to display the data as rows, where the row after data is having null values except on the description column.
DECLARE #StudentData TABLE
(
RowID INT NOT NULL PRIMARY KEY IDENTITY(1,1),
RemarksDate NVARCHAR(20),
StudentName NVARCHAR(1000),
Description NVARCHAR(MAX),
TotStudents NVARCHAR(100)
)
INSERT INTO #StudentData(RemarksDate, StudentName, Description, TotStudents)
VALUES('2/1/2021', NULL, 'Poor In English', '14'),
(NULL, NULL, '1 ABC', NULL),
(NULL, NULL, '1 XYZ', NULL),
(NULL, NULL, '1 MNO', NULL),
(NULL, NULL, '1 IGH', NULL),
(NULL, NULL, '10 KKK', NULL),
('2/1/2021', NULL, 'Poor In Maths', '5'),
(NULL, NULL, '5 PQR', NULL),
('2/8/2021', NULL, 'Poor In Social', '1'),
(NULL, NULL, '1 RST', NULL)
This results in the output as follows:
I have written the following query to group and display rows:
SELECT t1.RemarksDate, LTRIM(RIGHT(t2.Description, LEN(t2.Description) - PATINDEX('%[0-9][^0- 9]%', t2.Description ))) StudentName, t1.Description
,LEFT(t2.Description, PATINDEX('%[0-9][^0-9]%', t2.Description ))
FROM (
SELECT *, RowID + TotStudents MaxVal
FROM #StudentData
WHERE RemarksDate is NOT NULL
) t1
JOIN (
SELECT *
FROM #StudentData
WHERE RemarksDate is NULL
) t2 ON t2.RowId BETWEEN t1.RowID and t1.MaxVal
The data is displayed as follows
Expected output is as follows
2/1/2021 ABC Poor In English 1
2/1/2021 XYZ Poor In English 1
2/1/2021 MNO Poor In English 1
2/1/2021 IGH Poor In English 1
2/1/2021 KKK Poor In English 10
2/1/2021 PQR Poor In Maths 5
2/8/2021 RST Poor In Social 1
This is a type of gaps-and-islands problem. There are many solutions, I will give you one that only requires a single scan of the base table.
We have a header row and child rows, and we need to apply the header row values to the child rows.
We can solve this by defining the start point of each group, then taking windowed header values for each group and finally filtering out the header rows
WITH Groupings AS (
SELECT *,
GroupId = MAX(CASE WHEN Description LIKE 'Poor%' THEN RowID END)
OVER (ORDER BY RowID ROWS UNBOUNDED PRECEDING)
FROM #StudentData s
),
GroupValues AS (
SELECT
RemarksDate = MAX(CASE WHEN Description LIKE 'Poor%' THEN RemarksDate END)
OVER (PARTITION BY GroupId),
DescriptionHeader = MAX(CASE WHEN Description LIKE 'Poor%' THEN Description END)
OVER (PARTITION BY GroupId),
Space = CHARINDEX(' ', Description),
Description
FROM Groupings
)
SELECT
RemarksDate,
DescriptionHeader,
StudentName = SUBSTRING(Description, Space + 1, LEN(Description)),
SomeNumber = LEFT(Description, Space - 1)
FROM GroupValues
WHERE Description NOT LIKE 'Poor%';
db<>fiddle
Except the fact that the table design is pretty awful, I would suggest the following approach:
WITH cteRemarks AS(
SELECT *, LEAD(RowId) OVER (ORDER BY RowID) AS RowIdNxt
FROM #StudentData
WHERE TotStudents IS NOT NULL
)
SELECT r.RemarksDate
,RIGHT(t.Description, LEN(t.Description)-CHARINDEX(' ', t.Description)) AS StudentsName
,r.Description AS Description
,LEFT(t.Description, CHARINDEX(' ', t.Description)-1) AS Val
FROM cteRemarks r
LEFT JOIN #StudentData t ON t.TotStudents IS NULL
AND t.RowID > r.RowID
AND t.RowID < ISNULL(r.RowIDNxt, 99999999)

Splitting a string on comma and inserting it in a table as a row in sql

I have the following table in my database where two of the columns has comma separated string. I want to split the columns based on comma and insert the string in the database as a row. Below is my table and insert statements:
CREATE TABLE [dbo].[TABLEA](
[Id] [int] IDENTITY(1,1) NOT NULL,
[DocNumber] [varchar](50) NULL,
[InternalDocNumber] [varchar](50) NULL,
[Date] [varchar](50) NULL,
[DocType] [varchar](50) NULL,
[Description] [varchar](50) NULL,
[NameG] [varchar](max) NULL,
[NameGR] [varchar](max) NULL,
[NumberPages] [varchar](50) NULL)
Below are the insert statements in the table:
INSERT INTO [dbo].[TABLEA]
([DocNumber]
,[InternalDocNumber]
,[Date]
,[DocType]
,[Description]
,[NameG]
,[NameGR]
,[NumberPages])
VALUES
(1
,1235
,'12/23/2020'
,3
,'this is a test'
,'test1, test2, test3'
,'test6, test4'
,1),
(2
,3456
,'12/24/2020'
,3
,'this is a test1'
,'test4, test5, test6'
,'test9, test4'
,2)
,
(6
,6789
,'12/24/2020'
,3
,'this is a test3'
,'test9'
,'test100, test15, test16'
,2)
GO
From the above table. I want to create a new table that have a result like below:
ID DocNumber InternalDocnumber date DocType Description NameG NameGR NumberPage
1 1 1235 12/23/2020 3 thisisaTest test1 test6 1
1 1 1235 12/23/2020 3 thisisaTest test2 test4 1
1 1 1235 12/23/2020 3 thisisaTest test3 NULL 1
2 2 3456 12/24/2020 3 thisisaTest1 test4 test9 2
2 2 3456 12/24/2020 3 thisisaTest1 test5 test4 2
2 2 3456 12/24/2020 3 thisisaTest1 test6 NULL 2
3 6 6789 12/24/2020 3 thisisaTest3 test9 test100 2
3 6 6789 12/24/2020 3 thisisaTest3 NULL test15 2
3 6 6789 12/24/2020 3 thisisaTest3 NULL test16 2
Basically, I want the comma delimited string that is present in column NameG and NameGR to be splitted on comma based and then insert in a new table as anew row. The order is very important, if there is "Test1" in NameG column then here should be "Test6" in columnGR.
Any help with this will be highly appreciated.
This is a pain in SQL Server, because strint_split() doesn't provide a number or even a guaranteed ordering. So instead, use a recursive CTE:
with cte as (
select a.docnumber, convert(varchar(max), null) as gr, convert(varchar(max), null) as g,
convert(varchar(max), nameGR) as restGR, convert(varchar(max), nameG) as restG, 0 as lev
from tableA a
union all
select cte.docnumber,
left(restgr, charindex(',', restgr + ',') - 1) as gr,
left(restg, charindex(',', restg + ',') - 1) as g,
stuff(restgr, 1, charindex(',', restgr + ',') + 1, '') as restgr,
stuff(restg, 1, charindex(',', restg + ',') + 1, '') as restg,
lev + 1
from cte
where restgr > '' or restg > ''
)
select id, gr, g
from cte
where lev > 0;
Here is a db<>fiddle.
This only shows the one docnumber column. You can add the rest of the columns. I think they just confuse the presentation.
with gx as
(select [Id], [NameG],
row_number() over (partition by [id] order by [Id]) as rNo
from (select [Id] ,ltrim(x.Value) as [NameG]
from tablea
cross apply string_split(nameG, ',') as x) g),
grx as (
select [Id], [NameGR],
row_number() over (partition by [Id] order by [Id]) as rNo
from
(select [Id] ,ltrim(x.Value) as [NameGR]
from tablea
cross apply string_split(nameGR, ',') as x) gr),
names (Id, NameG, NameGR, r1, r2) as
( select coalesce(gx.Id, grx.Id), gx.NameG, grx.NameGR, coalesce(gx.rNo, grx.rNo), coalesce(grx.rNo, gx.rNo)
from gx
full join grx on gx.Id = grx.Id and gx.rNo = grx.rNo)
select a.Id
,[DocNumber]
,[InternalDocNumber]
,[Date]
,[DocType]
,[Description]
,n.[NameG]
,n.[NameGR]
,[NumberPages]
from tableA a
inner join names n on a.Id = n.Id
order by a.Id, r1, r2;
use the function Call STRING_SPLIT
SELECT ID,value AS NameG
FROM TABLEA
CROSS APPLY STRING_SPLIT(NameG, ',')
with this function the separated comma values ​​are separated but with the relation of the ID
then we look for the relationship with the id of tablea
WITH DataSplit as
(
SELECT ID,value AS NameG
FROM TABLEA
CROSS APPLY STRING_SPLIT(NameG, ',')
)
select TABLEA.Id,TABLEA.DocNumber,TABLEA.InternalDocNumber,TABLEA.Date,
TABLEA.DocType,TABLEA.Description, DataSplit.NameG
from TABLEA inner join DataSplit on TABLEA.id=DataSplit.id;
see the example in fiddler for more detail

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

Calc SUM total of pivoted table by two columns into predefined table?

Big thanks to #JohnCappelletti as he's shown how to pivot a table:
DECLARE #OperatorPrice TABLE (ID INT NOT NULL, OperatorId INT NULL, Price
NUMERIC(18,3) NULL, FName VARCHAR(50) NULL)
INSERT INTO #OperatorPrice (
ID, OperatorId, Price, FName
)
VALUES
(226, 996, 22954,'Operator1')
, (266, 1016, 79011.2, 'Operator3')
, (112, 1029, 14869, 'Operator4')
, (112, 996, 22954, 'Operator1')
, (93, 1031, 10568.96, 'Operator5')
DECLARE #TR TABLE
(
ID INT NULL ,
Operator1 DECIMAL(18,3) NULL, OC1 DECIMAL(18,3) NULL, Operator2 DECIMAL(18,3) NULL,
OC2 DECIMAL(18,3) NULL, Operator3 DECIMAL(18,3) NULL, OC3 DECIMAL(18,3) NULL,
Operator4 DECIMAL(18,3) NULL, OC4 DECIMAL(18,3) NULL, Operator5 DECIMAL(18,3) NULL,
OC5 DECIMAL(18,3) NULL
)
INSERT #TR
SELECT *
FROM (
Select A.ID
,B.*
From #OperatorPrice A
Cross Apply ( values (FName,Price)
,('OC'+replace(FName,'Operator',''),OperatorID)
) B (Item,Value)
Union All
Select ID=(select min(ID) From #OperatorPrice)
,B.*
From ( Select Top 50 N=Row_Number() Over (Order By (Select NULL))
From master..spt_values n1 ) A
Cross Apply ( values (concat('Operator',N),NULL)
,(concat('OC',N),NULL)
) B (Item,Value)
) AS SourceTable
PIVOT ( sum(Value) FOR Item IN (Operator1, OC1, Operator2, OC2,
Operator3, OC3, Operator4, OC4, Operator5, OC5) ) AS PivotTable
The above code works perfectly!
However, I would like the total sum for each column. So the desired ouput should looks like this:
ID Operator1 OC1 Operator2 OC2 Operator3 OC3 Operator4 OC4 Operator5 OC5
Total 45908.000 1992 NULL NULL NULL NULL NULL NULL NULL NULL
93 NULL NULL NULL NULL NULL NULL NULL NULL 10568.96 1031
112 22954.000 996.0 NULL NULL NULL NULL 14869.0 1029.000 NULL NULL
226 22954.000 996.0 NULL NULL NULL NULL 14869.0 1029.000 NULL NULL
266 NULL NULL NULL NULL 79011.200 1016.000 NULL NULL NULL NULL
or an image:
I've tried to use the following code:
INSERT #TR
SELECT
Total = SUM([Operator1] + [OC1] + [Operator2] + [OC2] + [Operator3] +
[OC3]+ [Operator4] + [OC4] + [Operator5] + [OC5])
, *
FROM (
Select A.ID
,B.*
From #OperatorPrice A
Cross Apply ( values (FName,Price)
,('OC'+replace(FName,'Operator',''),OperatorID)
) B (Item,Value)
Union All
Select ID=(select min(ID) From #OperatorPrice)
,B.*
From ( Select Top 50 N=Row_Number() Over (Order By (Select NULL)) From
master..spt_values n1 ) A
Cross Apply ( values (concat('Operator',N),NULL)
,(concat('OC',N),NULL)
) B (Item,Value)
) AS SourceTable
PIVOT ( sum(Value) FOR Item IN (Operator1, OC1, Operator2, OC2,
Operator3, OC3, Operator4, OC4, Operator5, OC5) ) AS PivotTable
But it doesn't work as it shows an error:
Msg 8120, Level 16, State 1, Line 24 Column 'PivotTable.ID' is
invalid in the select list because it is not contained in either an
aggregate function or the GROUP BY clause.
How can I get SUM of each column and put this row at the first place?
Notice
The ID in #TR is now a varchar(25)
Added two "TOTAL" rows in the CROSS APPLY
After the Union All, I changed the ID to "Total"
.
DECLARE #OperatorPrice TABLE (ID int NOT NULL, OperatorId INT NULL, Price
NUMERIC(18,3) NULL, FName VARCHAR(50) NULL)
INSERT INTO #OperatorPrice (
ID, OperatorId, Price, FName
)
VALUES
(226, 996, 22954,'Operator1')
, (266, 1016, 79011.2, 'Operator3')
, (112, 1029, 14869, 'Operator4')
, (112, 996, 22954, 'Operator1')
, (93, 1031, 10568.96, 'Operator5')
DECLARE #TR TABLE
(
ID varchar(25) NULL ,
Operator1 DECIMAL(18,3) NULL, OC1 DECIMAL(18,3) NULL, Operator2 DECIMAL(18,3) NULL,
OC2 DECIMAL(18,3) NULL, Operator3 DECIMAL(18,3) NULL, OC3 DECIMAL(18,3) NULL,
Operator4 DECIMAL(18,3) NULL, OC4 DECIMAL(18,3) NULL, Operator5 DECIMAL(18,3) NULL,
OC5 DECIMAL(18,3) NULL
)
INSERT #TR
SELECT *
FROM (
Select B.*
From #OperatorPrice A
Cross Apply ( values ('Total',FName,Price)
,('Total','OC'+replace(FName,'Operator',''),OperatorID)
,(convert(varchar(25),A.ID),FName,Price)
,(convert(varchar(25),A.ID),'OC'+replace(FName,'Operator',''),OperatorID)
) B (ID,Item,Value)
Union All
Select ID='Total'
,B.*
From ( Select Top 50 N=Row_Number() Over (Order By (Select NULL))
From master..spt_values n1 ) A
Cross Apply ( values (concat('Operator',N),NULL)
,(concat('OC',N),NULL)
) B (Item,Value)
) AS SourceTable
PIVOT ( sum(Value) FOR Item IN (Operator1, OC1, Operator2, OC2,
Operator3, OC3, Operator4, OC4, Operator5, OC5) ) AS PivotTable
Select * from #TR
Order by try_convert(int,ID)
Returns
You can use CTE to carry your pivot result set. UNION ALL combine SUM total result set and pivot result.
;with cte as (
SELECT *
FROM (
Select A.ID
,B.*
From #OperatorPrice A
Cross Apply ( values (FName,Price)
,('OC'+replace(FName,'Operator',''),OperatorID)
) B (Item,Value)
Union All
Select ID=(select min(ID) From #OperatorPrice)
,B.*
From ( Select Top 50 N=Row_Number() Over (Order By (Select NULL))
From master..spt_values n1 ) A
Cross Apply ( values (concat('Operator',N),NULL)
,(concat('OC',N),NULL)
) B (Item,Value)
) AS SourceTable
PIVOT ( sum(Value) FOR Item IN (Operator1, OC1, Operator2, OC2,
Operator3, OC3, Operator4, OC4, Operator5, OC5) ) AS PivotTable
)
INSERT #TR
SELECT
NULL,
SUM(Operator1),
SUM(OC1),
SUM(Operator2),
SUM(OC2),
SUM(Operator3),
SUM(OC3),
SUM(Operator4),
SUM(OC4),
SUM(Operator5),
SUM(OC5)
FROM CTE
UNION ALL
SELECT ID,Operator1,OC1,Operator2,OC2,Operator3,OC3,Operator4,OC4,Operator5,OC5
FROM cte
sqlfiddle