How To Pivot table using SQL server - sql-server

i creating SQL table its have three column fields
NAME TYPE Amount
RAJ 401K % 500
Kumar ROTH 600
RAJ 401K % 500
Kumar ROTH 700
Karthi 401K % 1500
RAj Roth 800
Tony DPD 8
above table i have three column Fields Name and Type finally Amount. i try convert this table like separate columns Type. in my table Raj come in table many time so i want to sum Raj all value which values come with 401K table finally its show single value. its added 401k value. Roth value seperate like this
Name 401k% Roth Amount
Raj 1000 800 1800
Kumar 0 1300 1300
Karthi 1500 0 1500.
Nimi 0 0 0 -- this is should Remove only value come with only 401k% and Roth . other value should be delete .
i want like this. i am trying to achive this using Pivot talbe i am getting Null value
select [Name],[401k%] ,[Roth%]
from DeductionHistory
PIVOT
( SUM(Amount) FOR DeductionName IN([401k%],[Roth%])
)
AS Pivottable
please any one help me . how to get value Like this

CREATE TABLE #TABLE111
([NAME] VARCHAR(6), [TYPE] VARCHAR(6), [AMOUNT] INT)
INSERT INTO #TABLE111
([NAME], [TYPE], [AMOUNT])
VALUES
('RAJ', '401K %', 500),
('KUMAR', 'ROTH', 600),
('RAJ', '401K %', 500),
('KUMAR', 'ROTH', 700),
('KARTHI', '401K %', 1500),
('RAJ', 'ROTH', 800)
SELECT NAME ,ISNULL([401K %],0) [401K %] ,ISNULL([ROTH],0) [ROTH] ,ISNULL([401K %],0)+ISNULL([ROTH],0) 'TOTAL'
FROM
(
SELECT *
FROM #TABLE111
) SRC
PIVOT
(
SUM([AMOUNT])
FOR [TYPE] IN ([401K %], [ROTH] )
) PIV
ORDER BY NAME DESC
output
NAME 401K % ROTH TOTAL
RAJ 1000 800 1800
Kumar 0 1300 1300
Karthi 1500 0 1500

--I have included DPD the query
DECLARE #DeductionHistory TABLE (NAME VARCHAR(20), [TYPE] VARCHAR(20),Amount MONEY)
INSERT INTO #DeductionHistory VALUES
('RAJ','401K%',500),
('Kumar','ROTH',600),
('RAJ','401K%',500),
('Kumar','ROTH',700),
('Karthi','401K%',1500),
('RAj','ROTH',800),
('Tony' ,'DPD', 8)
/*****Use a VTE or temp table*****/
;WITH DeductionHistory
AS(
SELECT * FROM #DeductionHistory
PIVOT
(
SUM(Amount) FOR [TYPE] IN ([401K%],[ROTH],[DPD])
) PV1
)
SELECT
[NAME]
,ISNULL([401K%],0) AS [401K%]
,ISNULL([ROTH],0) AS [ROTH]
,ISNULL([DPD],0) AS [DPD]
,ISNULL([401K%],0)+ISNULL([ROTH],0)+ISNULL([DPD],0) AS Amount
FROM DeductionHistory
OUTPUT
NAME 401K% ROTH DPD Amount
Karthi 1500.00 0.00 0.00 1500.00
Kumar 0.00 1300.00 0.00 1300.00
RAJ 1000.00 800.00 0.00 1800.00
Tony 0.00 0.00 8.00 8.00

Related

Recursive query to use a date returned in initial query as limit in subsequent query

I have a business need to project when a specific task needs to be done based on the usage of a task.
For example, you need to change the oil in your car every 3000 miles. Some days you drive 300 miles, and other days you drive 500 miles. When you hit 3000, you change the oil, and restart the counter. Based on a projected usage table, return a set of all the oil change dates.
I could do this in a table-valued function or some other 'coded' solution.
But I thought I could do it in one statement, a recursive cte perhaps.
I'm having difficulties 'joining' the next date into the WHERE of the recursive part.
And SQL doesn't like 'TOP 1' in a recursive CTE at all. :)
I would like a set like this:
This is what I've got:
WITH cte_MilesMX (RateDate,RunningRateMiles)
AS
(
-- Initial query
SELECT TOP 1 *
FROM (
SELECT
RateDate,
SUM(RateMiles) OVER (ORDER BY RateDate) AS RunningRateMiles
FROM dbo.RatesbyDay
WHERE RateDate > '2020-01-01') q1
WHERE q1.RunningRateMiles >= 3000
UNION ALL
-- Recursive part
SELECT TOP 1 *
FROM (
SELECT
rbd.RateDate,
SUM(RateMiles) OVER (ORDER BY rbd.RateDate) AS RunningRateMiles
FROM dbo.RatesbyDay rbd
JOIN cte_MilesMX cte
ON 1 = 1
WHERE rbd.RateDate > cte.RateDate) q1
WHERE q1.RunningRateMiles >= 3000
)
SELECT *
FROM cte_MilesMX
If you want to fool with this, here is the example:
Any help would be greatly appreciated.
Thanks.
CREATE TABLE RatesbyDay(
RateDate DATE,
RateMiles INT);
INSERT INTO RatesbyDay VALUES ('2020-01-01',600)
INSERT INTO RatesbyDay VALUES ('2020-01-02',450)
INSERT INTO RatesbyDay VALUES ('2020-01-03',370)
INSERT INTO RatesbyDay VALUES ('2020-01-04',700)
INSERT INTO RatesbyDay VALUES ('2020-01-05',100)
INSERT INTO RatesbyDay VALUES ('2020-01-06',480)
INSERT INTO RatesbyDay VALUES ('2020-01-07',430)
INSERT INTO RatesbyDay VALUES ('2020-01-08',200)
INSERT INTO RatesbyDay VALUES ('2020-01-09',590)
INSERT INTO RatesbyDay VALUES ('2020-01-10',380)
INSERT INTO RatesbyDay VALUES ('2020-01-11',220)
INSERT INTO RatesbyDay VALUES ('2020-01-12',320)
INSERT INTO RatesbyDay VALUES ('2020-01-13',360)
INSERT INTO RatesbyDay VALUES ('2020-01-14',600)
INSERT INTO RatesbyDay VALUES ('2020-01-15',450)
INSERT INTO RatesbyDay VALUES ('2020-01-16',475)
INSERT INTO RatesbyDay VALUES ('2020-01-17',300)
INSERT INTO RatesbyDay VALUES ('2020-01-18',190)
INSERT INTO RatesbyDay VALUES ('2020-01-19',435)
INSERT INTO RatesbyDay VALUES ('2020-01-20',285)
INSERT INTO RatesbyDay VALUES ('2020-01-21',350)
INSERT INTO RatesbyDay VALUES ('2020-01-22',410)
INSERT INTO RatesbyDay VALUES ('2020-01-23',250)
INSERT INTO RatesbyDay VALUES ('2020-01-24',300)
INSERT INTO RatesbyDay VALUES ('2020-01-25',250)
INSERT INTO RatesbyDay VALUES ('2020-01-26',650)
INSERT INTO RatesbyDay VALUES ('2020-01-27',180)
INSERT INTO RatesbyDay VALUES ('2020-01-28',280)
INSERT INTO RatesbyDay VALUES ('2020-01-29',200)
INSERT INTO RatesbyDay VALUES ('2020-01-30',100)
INSERT INTO RatesbyDay VALUES ('2020-01-31',100)
-- this returns the 1st oil change assuming we just changed it on 1-1-2020
SELECT TOP 1 *
FROM (
SELECT
RateDate,
SUM(RateMiles) OVER (ORDER BY RateDate) AS RunningRateMiles
FROM dbo.RatesbyDay
WHERE RateDate > '2020-01-01') q1
WHERE q1.RunningRateMiles >= 3000
-- the above query returned 1-9-2020 as the oil change, so when is the next one.
SELECT TOP 1 *
FROM (
SELECT
RateDate,
SUM(RateMiles) OVER (ORDER BY RateDate) AS RunningRateMiles
FROM dbo.RatesbyDay
WHERE RateDate > '2020-01-09') q1
WHERE q1.RunningRateMiles >= 3000
-- etc. etc.
SELECT TOP 1 *
FROM (
SELECT
RateDate,
SUM(RateMiles) OVER (ORDER BY RateDate) AS RunningRateMiles
FROM dbo.RatesbyDay
WHERE RateDate > '2020-01-17') q1
WHERE q1.RunningRateMiles >= 3000
SELECT TOP 1 *
FROM (
SELECT
RateDate,
SUM(RateMiles) OVER (ORDER BY RateDate) AS RunningRateMiles
FROM dbo.RatesbyDay
WHERE RateDate > '2020-01-26') q1
WHERE q1.RunningRateMiles >= 3000
This isn't a recursive CTE but it does do what you're what you're trying to do. The technique goes by a couple different names... Usually either "Quirky Update" or "Ordered Update".
First thing, notice that I added two new columns to your table and a clustered index. They are in fact necessary but if are unwilling or unable to modify the existing table, this works just as well with a #TempTable.
For more detailed information, see Solving the Running Total and Ordinal Rank Problems (Rewritten)
Also... fair warning, this technique isn't without it's detractors due to the fact that Microsoft doesn't guarantee that it will work as expected.
USE tempdb;
GO
IF OBJECT_ID('tempdb.dbo.RatesByDay', 'U') IS NOT NULL
BEGIN DROP TABLE tempdb.dbo.RatesByDay; END;
GO
CREATE TABLE tempdb.dbo.RatesByDay (
RateDate date NOT NULL
CONSTRAINT pk_RatesByDay PRIMARY KEY CLUSTERED (RateDate), -- clustered index is needed to control the direction of the update.
RateMiles int NOT NULL,
IsChangeDay bit NULL,
MilesSinceLastChange int NULL
);
GO
INSERT tempdb.dbo.RatesByDay (RateDate, RateMiles) VALUES
('2020-01-01',600),('2020-01-02',450),('2020-01-03',370),('2020-01-04',700),('2020-01-05',100),('2020-01-06',480),
('2020-01-07',430),('2020-01-08',200),('2020-01-09',590),('2020-01-10',380),('2020-01-11',220),('2020-01-12',320),
('2020-01-13',360),('2020-01-14',600),('2020-01-15',450),('2020-01-16',475),('2020-01-17',300),('2020-01-18',190),
('2020-01-19',435),('2020-01-20',285),('2020-01-21',350),('2020-01-22',410),('2020-01-23',250),('2020-01-24',300),
('2020-01-25',250),('2020-01-26',650),('2020-01-27',180),('2020-01-28',280),('2020-01-29',200),('2020-01-30',100),
('2020-01-31',100);
--=====================================================================================================================
DECLARE
#RunningMiles int = 0,
#Anchor date;
UPDATE rbd SET
#RunningMiles = rbd.MilesSinceLastChange = CASE WHEN #RunningMiles < 3000 THEN #RunningMiles ELSE 0 END + rbd.RateMiles,
rbd.IsChangeDay = CASE WHEN #RunningMiles < 3000 THEN 0 ELSE 1 END,
#Anchor = rbd.RateDate
FROM
dbo.RatesByDay rbd WITH (TABLOCKX, INDEX (1))
WHERE 1 = 1
AND rbd.RateDate > '2020-01-01'
OPTION (MAXDOP 1);
-------------------------------------
SELECT * FROM dbo.RatesByDay rbd;
And the results...
RateDate RateMiles IsChangeDay MilesSinceLastChange
---------- ----------- ----------- --------------------
2020-01-01 600 NULL NULL
2020-01-02 450 0 450
2020-01-03 370 0 820
2020-01-04 700 0 1520
2020-01-05 100 0 1620
2020-01-06 480 0 2100
2020-01-07 430 0 2530
2020-01-08 200 0 2730
2020-01-09 590 1 3320
2020-01-10 380 0 380
2020-01-11 220 0 600
2020-01-12 320 0 920
2020-01-13 360 0 1280
2020-01-14 600 0 1880
2020-01-15 450 0 2330
2020-01-16 475 0 2805
2020-01-17 300 1 3105
2020-01-18 190 0 190
2020-01-19 435 0 625
2020-01-20 285 0 910
2020-01-21 350 0 1260
2020-01-22 410 0 1670
2020-01-23 250 0 1920
2020-01-24 300 0 2220
2020-01-25 250 0 2470
2020-01-26 650 1 3120
2020-01-27 180 0 180
2020-01-28 280 0 460
2020-01-29 200 0 660
2020-01-30 100 0 760
2020-01-31 100 0 860
You can do this with a recursive query:
with
data as (select r.*, row_number() over(order by ratedate) rn from ratesbyday r),
cte as (
select d.*, ratemiles total, ratemiles newtotal from data d where rn = 1
union all
select d.*,
c.newtotal + d.ratemiles,
case when c.newtotal < 3000 and c.newtotal + d.ratemiles >= 3000 then 0 else c.newtotal + d.ratemiles end
from cte c
inner join data d on d.rn = c.rn + 1
)
select ratedate, ratemiles, total
from cte
where newtotal = 0
order by ratedate
The query starts by enumerating the rows. Then, it iteratively walks them, starting from the "first" one; everytime we exceed the 3000 miles threshold, we reset the running miles count. We can then filter on "reset" rows.
Demo on DB Fiddle:
ratedate | ratemiles | total
:--------- | --------: | ----:
2020-01-07 | 430 | 3130
2020-01-15 | 450 | 3120
2020-01-25 | 250 | 3245
If there may be more than 100 rows in your dataset, you need to add option (maxrecursion 0) at the very end of the query.
In this instance I would use a rolling agg and then use the mod operator to find the points where it hits the 3000 interval.
Using the table desc and inserts above here is an example:
-- When the mod value "resets" then the oil change is due, check this using LAG
SELECT
agg.RateDate
,agg.RateMiles
,agg.MilesAgg
,agg.MilesAgg%3000 AS ModValue
,CASE WHEN agg.MilesAgg%3000 < LAG(agg.MilesAgg) OVER(ORDER BY agg.RateDate)%3000
THEN 'Due'
ELSE 'NotDue'
END
FROM
(
--Get the rolling total of miles
SELECT
rbd.RateDate
,rbd.RateMiles
,SUM(rbd.RateMiles) OVER(ORDER BY rbd.RateDate ROWS UNBOUNDED PRECEDING) AS MilesAgg
FROM #RatesByDay rbd
) agg
Results, first day is counting the 600 miles as being AFTER the oil change
RateDate Mi MiAgg Mod IsDue?
--------------------------------------
2020-01-01 600 600 600 NotDue
2020-01-02 450 1050 1050 NotDue
2020-01-03 370 1420 1420 NotDue
2020-01-04 700 2120 2120 NotDue
2020-01-05 100 2220 2220 NotDue
2020-01-06 480 2700 2700 NotDue
2020-01-07 430 3130 130 Due
2020-01-08 200 3330 330 NotDue
2020-01-09 590 3920 920 NotDue
2020-01-10 380 4300 1300 NotDue
2020-01-11 220 4520 1520 NotDue
2020-01-12 320 4840 1840 NotDue
2020-01-13 360 5200 2200 NotDue
2020-01-14 600 5800 2800 NotDue
2020-01-15 450 6250 250 Due
2020-01-16 475 6725 725 NotDue
2020-01-17 300 7025 1025 NotDue
2020-01-18 190 7215 1215 NotDue
2020-01-19 435 7650 1650 NotDue
2020-01-20 285 7935 1935 NotDue
2020-01-21 350 8285 2285 NotDue
2020-01-22 410 8695 2695 NotDue
2020-01-23 250 8945 2945 NotDue
2020-01-24 300 9245 245 Due
2020-01-25 250 9495 495 NotDue
2020-01-26 650 10145 1145 NotDue
2020-01-27 180 10325 1325 NotDue
2020-01-28 280 10605 1605 NotDue
2020-01-29 200 10805 1805 NotDue
2020-01-30 100 10905 1905 NotDue
2020-01-31 100 11005 2005 NotDue

How can i implement this query in sql server?

I'm beginner in sql server,have two table ,table GetOnlineBills with this shape:
--Telno-- --Cycle-- --Bill--
nchar(20) nchar(20) float
And table 25Percantage:
--Telno-- --Cycle-- --Bill--
nchar(20) nchar(20) float
want to write query or tsql for analysis Telno in GetOnlineBill table with this flow:
1-fetch Telno ang get it bill value in Cycle 951 and Cycle 952
2-If Bill(952)>Bill(951) then write into 25 percantage this data (Bill(952)-Bill(951))
into GetOnlineBill's table write subscribe Bill information per cycle,For example have two subscribe(in really world have 1 million subscribe) with this plan:
into the GetOnlineBills one mounth data is this:
--Telno-- --Cycle-- --Bill--
12345 951 300
54321 951 500
and other month data is :
--Telno-- --Cycle-- --Bill--
12345 952 400
54321 952 600
and final GetOnlineBill for two mounth is:
--Telno-- --Cycle-- --Bill--
12345 951 300
54321 951 500
12345 952 400
54321 952 600
Now,i want analysis GetOnlineBill,want write query or tsql to create final 25Percantage table show me this:
--Telno-- --Cycle-- --Bill--
12345 951-952 100 ------>Bill(952)-Bill(951) Minus
54321 951-952 300
----------------------------------------
Explain:
Bill:Field Name on GetOnlineBill table
952:Cycle
951:Cycle
How can i write query or tsql for that purpose?please write and post me.thanks all.
Inner join should help you:
SELECT y.Telno,
t.Cycle+'-'+y.Cycle Cycle,
y.Bill - t.Bill Price
FROM YourTable y
INNER JOIN YourTable t
ON y.Telno = t.TelNo AND CAST(y.Cycle as int)-1 = CAST(t.Cycle as int)
Output:
Telno Cycle Price
12345 951-952 100
54321 951-952 100
If Cycle column can not be converted to integer then you need some other field to order table right way and do join with that field (or OUTER APPLY)
Try as the below:
DECLARE #Tbl TABLE (TelNo NCHAR(20), Cycle NCHAR(20), Bill FLOAT)
INSERT INTO #Tbl
VALUES
('12345', '951', 300),
('12345', '952', 400),
('54321', '951', 500),
('54321', '952', 600)
;WITH CTE
AS
(
SELECT *, DENSE_RANK() OVER (ORDER BY Cycle) MonthId
FROM #Tbl
)
SELECT
CurrentMonth.TelNo,
CurrentMonth.Cycle + '-' + NextMonth.Cycle Cycle,
CASE
WHEN NextMonth.Bill > CurrentMonth.Bill THEN NextMonth.Bill - CurrentMonth.Bill
ELSE CurrentMonth.Bill END Bill -- Change as you want
FROM
CTE CurrentMonth LEFT JOIN
CTE NextMonth ON CurrentMonth.TelNo = NextMonth.TelNo AND (NextMonth.MonthId - 1) = CurrentMonth.MonthId
WHERE
NextMonth.MonthId IS NOT NULL
Result:
TelNo Cycle Bill
-------------------- ------------------------ ---------
54321 951-952 100
12345 951-952 100

get difference between two rows and populate the difference value

enter image description hereI have a table with the below structure.
[![enter image description here][2]][2]
I want to calculate one more column name it has "Difference". The difference should be calculated if the guid values are same and populate the difference on both rows.
After the Difference column my table should look like below
GIving more details
My table looks like below
GUID Date Quantity
0001639C-8047-45FD-8FB0-D24B906D25D0 7/21/2016 30
0001639C-8047-45FD-8FB0-D24B906D25D0 7/15/2016 20
00088951-A2F6-4405-9195-4E830912D56D 7/22/2016 40
00088951-A2F6-4405-9195-4E830912D56D 7/12/2016 20
00060D8A-F711-42BD-824F-6F9F92A02E6E 7/23/2016 2
00074492-6068-48A6-8F99-F70D7328B166 7/19/2016 15
0007E203-4BD9-4937-BFCB-6A3EBCA33448 7/15/2016 2
After Calculating the difference column it should look like below
GUID Date Quantity Difference
0001639C-8047-45FD-8FB0-D24B906D25D0 7/21/2016 30 10
0001639C-8047-45FD-8FB0-D24B906D25D0 7/15/2016 20 10
00088951-A2F6-4405-9195-4E830912D56D 7/22/2016 40 20
00088951-A2F6-4405-9195-4E830912D56D 7/12/2016 20 20
00060D8A-F711-42BD-824F-6F9F92A02E6E 7/23/2016 2 0
00074492-6068-48A6-8F99-F70D7328B166 7/19/2016 15 0
0007E203-4BD9-4937-BFCB-6A3EBCA33448 7/15/2016 2 0
The difference column should be calculated when the guid values are same ,the difference in the quantity should be performed and it should be populated in the difference column .If the guid values are not same then it should be populated with zero.
DECLARE #Table Table (GUID varchar(50), Date Date, Quantity Decimal(9,2))
Insert Into #Table (GUID,Date,Quantity) Values
('0001639C-8047-45FD-8FB0-D24B906D25D0','2016-07-21',30),
('0001639C-8047-45FD-8FB0-D24B906D25D0','2016-07-15',20),
('00088951-A2F6-4405-9195-4E830912D56D','2016-07-22',40),
('00088951-A2F6-4405-9195-4E830912D56D','2016-07-12',20),
('00060D8A-F711-42BD-824F-6F9F92A02E6E','2016-07-23',2),
('00074492-6068-48A6-8F99-F70D7328B166','2016-07-19',15),
('0007E203-4BD9-4937-BFCB-6A3EBCA33448','2016-07-15',2)
;with cteBase as (
Select *
,Difference=Quantity - Lag(Quantity) over (Partition By GUID Order By Date)
,PctChange =(100*(Lag(Quantity) over (Partition By GUID Order By Date)))/Quantity
From #Table
)
Select GUID,Date,Quantity
,Difference = sum(isnull(Difference,0)) over (Partition By GUID Order By Date Desc)
,PctChange = sum(isnull(PctChange ,0)) over (Partition By GUID Order By Date Desc)
From cteBase
Order by GUID,Date Desc
Returns
GUID Date Quantity Difference PctChange
0001639C-8047-45FD-8FB0-D24B906D25D0 2016-07-21 30.00 10.00 66.666666666666
0001639C-8047-45FD-8FB0-D24B906D25D0 2016-07-15 20.00 10.00 66.666666666666
00060D8A-F711-42BD-824F-6F9F92A02E6E 2016-07-23 2.00 0.00 0.000000000000
00074492-6068-48A6-8F99-F70D7328B166 2016-07-19 15.00 0.00 0.000000000000
0007E203-4BD9-4937-BFCB-6A3EBCA33448 2016-07-15 2.00 0.00 0.000000000000
00088951-A2F6-4405-9195-4E830912D56D 2016-07-22 40.00 20.00 50.000000000000
00088951-A2F6-4405-9195-4E830912D56D 2016-07-12 20.00 20.00 50.000000000000
DECLARE #Table Table (GUID varchar(50), Date Date, Quantity Decimal(9,2))
Insert Into #Table (GUID,Date,Quantity) Values
('0001639C-8047-45FD-8FB0-D24B906D25D0','2016-07-21',30),
('0001639C-8047-45FD-8FB0-D24B906D25D0','2016-07-15',20),
('00088951-A2F6-4405-9195-4E830912D56D','2016-07-22',40),
('00088951-A2F6-4405-9195-4E830912D56D','2016-07-12',20),
('00060D8A-F711-42BD-824F-6F9F92A02E6E','2016-07-23',2),
('00074492-6068-48A6-8F99-F70D7328B166','2016-07-19',15),
('0007E203-4BD9-4937-BFCB-6A3EBCA33448','2016-07-15',2)
;with cteBase as (
Select *
,Difference=Quantity - Lag(Quantity) over (Partition By GUID Order By Date)
From #Table
)
Select GUID,Date,Quantity
,Difference = sum(isnull(Difference,0)) over (Partition By GUID Order By Date Desc)
From cteBase
Order by GUID,Date Desc
Returns
GUID Date Quantity Difference
0001639C-8047-45FD-8FB0-D24B906D25D0 2016-07-21 30.00 10.00
0001639C-8047-45FD-8FB0-D24B906D25D0 2016-07-15 20.00 10.00
00060D8A-F711-42BD-824F-6F9F92A02E6E 2016-07-23 2.00 0.00
00074492-6068-48A6-8F99-F70D7328B166 2016-07-19 15.00 0.00
0007E203-4BD9-4937-BFCB-6A3EBCA33448 2016-07-15 2.00 0.00
00088951-A2F6-4405-9195-4E830912D56D 2016-07-22 40.00 20.00
00088951-A2F6-4405-9195-4E830912D56D 2016-07-12 20.00 20.00

If one value exists then remove another

I'm stuck in my query on how to remove or rather skip a post if another one exists.
This is my table.
if L_ID column have value 821 AND 201 for the same P_ID then "remove" or donĀ“t use 201 and sum() then time
This would make P_ID 80 and 946 only have 2 rows.
This is probably easier than I think but I'm stuck.
Try it like this:
CREATE TABLE #YourTable(P_ID INT, L_ID INT, [Date] Date, [Time] DECIMAL(6,2));
INSERT INTO #YourTable VALUES
(80,201,{d'2015-08-01'},24.0)
,(80,821,{d'2015-08-01'},24.0)
,(80,822,{d'2015-08-01'},32.0)
,(946,201,{d'2015-08-01'},16.0)
,(946,821,{d'2015-08-01'},16.0)
,(946,819,{d'2015-08-01'},6.65)
,(6758,201,{d'2015-08-01'},7.25)
,(6758,200,{d'2015-08-01'},7.25)
;
--Test output
SELECT * FROM #YourTable;
--Set the SUMs in those lines with L_ID=821
UPDATE #YourTable SET [Time]=(SELECT SUM(x.[Time])
FROM #YourTable AS x
WHERE x.P_ID =#YourTable.[P_ID]
AND x.L_ID IN (821,201))
WHERE #YourTable.L_ID=821
--Delete the rows with L_ID=201 if there is one with 821 too
DELETE FROM #YourTable
WHERE L_ID = 201 AND EXISTS(SELECT * FROM #YourTable AS x
WHERE x.P_ID = #YourTable.P_ID AND x.L_ID =821 ) --The ID was wrong here, sorry...
--Test output
SELECT * FROM #YourTable;
--Clean up
DROP TABLE #YourTable;
Result:
P_ID L_ID Date Time
80 821 2015-08-01 48.00
80 822 2015-08-01 32.00
946 821 2015-08-01 32.00
946 819 2015-08-01 6.65
6758 201 2015-08-01 7.25
6758 200 2015-08-01 7.25
Try this code:
SELECT *,
SUM(Time) OVER(PARTITION BY P_ID, L_ID, Date) AS 'Sum'
FROM Your_Table
WHERE L_ID <> 201
AND P_ID NOT IN (
SELECT E1.P_ID
FROM Your_Table E1
INNER JOIN Your_Table E2
ON E1.P_ID = E2.P_ID
WHERE E1.L_ID = 821 AND E2.L_ID = 201)

Struggling with a dynamic pivot on multiple columns with one being concatenated

I have a table with data like so:
Employee PRDate Type Code Amount Subject Eligible
1234 1/1/2015 D 1 100.00 100.00 0.00
1234 1/1/2015 D 2 200.00 0.00 0.00
5678 1/1/2015 D 1 500.00 40.00 500.00
1234 1/1/2015 E 1 300.00 30.00 300.00
5678 1/1/2015 E 1 700.00 700.00 500.00
1234 1/1/2015 E 2 400.00 200.00 0.00
1234 1/8/2015 L 55 40.00 40.00 40.00
And I need for the data to be displayed like this:
Employee PRDate D1Amt D1Subj D1Elig D2Amt D2Subj D2Elig E1Amt E1Subj E1Elig E2Amt E2Subj E2Elig L55Amt L55Subj L55Elig
1234 1/1/2015 100.00 100.00 0.00 200.00 0.00 0.00 300.00 30.00 300.00 400.00 200.00 0.00 40.00 40.00 40.00
4678 1/1/2015 500.00 40.00 500.00 700.00 700.00 500.00
I can pivot on one column but when I try combining the Type and Code columns to get the one I get conversion errors (Type is a varchar and code is a tinyint). I'm not sure how to get to the desired results other than dynamic pivot. Can the desired results be achieved?
I've gotten this far but I can't figure out how to combine the type, code and each money columns (amount, subject and eligible) to get the data under the correct columns.
IF EXISTS (
SELECT *
FROM sys.tables
WHERE name LIKE '#temp285865%')
DROP TABLE #temp285865;
Create table dbo.#temp285865
(
EDLCodetemp varchar(10)
);
INSERT INTO #temp285865
(
[EDLCodetemp]
)
SELECT DISTINCT EDLCode
FROM #results
ORDER BY EDLCode;
-- Building a comma separated list of EDLCodes in #edltemp
DECLARE #cols varchar(1000);
SELECT #cols = COALESCE(#cols + ',[' + [EDLCodetemp] + ']', '[' + [EDLCodetemp] + ']')
FROM #temp285865;
-- Building the query appending columns
DECLARE #query varchar(4000);
SET #query =
'SELECT [CoName],
[PRCo],
[PRGroup],
[PREndDate],
[PaySeq],
[EDLType],
[Hours],
[SubjectAmt],
[EligibleAmt],
[PaidMth],
[LastName],
[FirstName],
[UseOver],
[OverAmt],
[Amount],
[PRGRDescrip],
[LimitPeriod],
[LimitMth],
[PREHEmployee],
[SortName],
[PaybackAmt],
[PaybackOverAmt],
[PaybackOverYN],
[PRDTEmployee],
[TrueEarns], '
+ #cols + ' FROM
(
SELECT [CoName],
[PRCo],
[PRGroup],
[PREndDate],
[PaySeq],
[EDLType],
[Hours],
[SubjectAmt],
[EligibleAmt],
[PaidMth],
[LastName],
[FirstName],
[PRDLDescrip],
[PRECDescrip],
[UseOver],
[OverAmt],
[Amount],
[PRGRDescrip],
[LimitPeriod],
[LimitMth],
[PREHEmployee],
[SortName],
[PaybackAmt],
[PaybackOverAmt],
[PaybackOverYN],
[PRDTEmployee],
[TrueEarns],
[EDLCode]
FROM #results
) p
PIVOT (
MAX(EDLCode)
FOR [EDLCode] IN (' + #cols + ')
)
as pvt';
EXEC(#query);
DROP TABLE #temp285865;
The following PIVOT with dynamic SQL would give you the result you want, based on the input data you provided (I changed the PRDate in the last row though).
The first statement builds an intermediate table #bt with the column names you want and the associated value. Then the column names are built in #cols for the dynamic SQL statement. Finally the intermediate table #bt is pivoted with a dynamic SQL statement using the #cols to pivot.
SET NOCOUNT ON;
CREATE TABLE #t(
Employee INT,
PRDate DATETIME,
Type CHAR(1),
Code TINYINT,
Amount DECIMAL(28,2),
Subject DECIMAL(28,2),
Eligible DECIMAL(28,2)
);
INSERT INTO #t(Employee,PRDate,Type,Code,Amount,Subject,Eligible)VALUES(1234,'2015-01-01','D',1,100.00,100.00,0.00);
INSERT INTO #t(Employee,PRDate,Type,Code,Amount,Subject,Eligible)VALUES(1234,'2015-01-01','D',2,200.00,0.00,0.00);
INSERT INTO #t(Employee,PRDate,Type,Code,Amount,Subject,Eligible)VALUES(5678,'2015-01-01','D',1,500.00,40.00,500.00);
INSERT INTO #t(Employee,PRDate,Type,Code,Amount,Subject,Eligible)VALUES(1234,'2015-01-01','E',1,300.00,30.00,300.00);
INSERT INTO #t(Employee,PRDate,Type,Code,Amount,Subject,Eligible)VALUES(5678,'2015-01-01','E',1,700.00,700.00,500.00);
INSERT INTO #t(Employee,PRDate,Type,Code,Amount,Subject,Eligible)VALUES(1234,'2015-01-01','E',2,400.00,200.00,0.00);
INSERT INTO #t(Employee,PRDate,Type,Code,Amount,Subject,Eligible)VALUES(1234,'2015-01-01','L',55,40.00,40.00,40.00);
SELECT
Employee,
PRDate,
Type+CAST(Code AS VARCHAR(3))+ca.name AS colname,
ca.val
INTO
#bt
FROM
#t
CROSS APPLY(
SELECT Amount AS val,'Amt' AS name
UNION ALL
SELECT Subject AS val,'Subj' AS name
UNION ALL
SELECT Eligible AS val,'Elig' AS name
) AS ca;
/* If you need to SUM for all dates, instead use this statement to create #bt
SELECT
Employee,
Type+CAST(Code AS VARCHAR(3))+ca.name AS colname,
ca.val
INTO
#bt
FROM
(
SELECT
Employee,
Type,
Code,
SUM(Amount) AS Amount,
SUM(Subject) AS Subject,
SUM(Eligible) AS Eligible
FROM
#t
GROUP BY
Employee,
Type,
Code
) AS t
CROSS APPLY(
SELECT Amount AS val,'Amt' AS name
UNION ALL
SELECT Subject AS val,'Subj' AS name
UNION ALL
SELECT Eligible AS val,'Elig' AS name
) AS ca;
*/
DECLARE #cols VARCHAR(8000);
SET #cols=STUFF(
(SELECT DISTINCT
',['+colname+']'
FROM
#bt
FOR XML PATH('')),
1,
1,
''
);
DECLARE #sql VARCHAR(MAX);
SET #sql='
SELECT
*
FROM
#bt
PIVOT(
MAX(val)
FOR colname IN ('+#cols+')
) AS piv
';
EXEC (#sql);
DROP TABLE #bt;
DROP TABLE #t;
The result is the following:
Employee PRDate D1Amt D1Elig D1Subj D2Amt D2Elig D2Subj E1Amt E1Elig E1Subj E2Amt E2Elig E2Subj L55Amt L55Elig L55Subj
1234 2015-01-01 100.00 0.00 100.00 200.00 0.00 0.00 300.00 300.00 30.00 400.00 0.00 200.00 40.00 40.00 40.00
5678 2015-01-01 500.00 500.00 40.00 NULL NULL NULL 700.00 500.00 700.00 NULL NULL NULL NULL NULL NULL

Resources