Group by and sum based on column values without sum() over()? - sql-server

We have a table [Kpis] that looks like the following:
RawId EmpId Date Hour Min KpiValue KpiName
106 ABC123 20160310 8 0 3 Kpi1
124 ABC123 20160310 8 0 65 Kpi1
121 ABC123 20160310 8 15 12 Kpi2
109 ABC109 20160310 8 0 34 Kpi2
112 ABC908 20160310 9 5 3 Kpi1
118 ABC907 20160310 8 30 24 Kpi1
115 ABC123 20160310 8 15 54 Kpi1
I would like to group by EmpId, KpiName, Date, Hour. So, for example, with this data, Kpi1 for EmpId ABC123 at Hour 8 would be 122.
So I tried using the CASE statement, but the result is incorrect. I haven't checked the actual totals in the result, but the sums should be correct. It's the format of the result that's incorrect; every empid has two rows: one for Kpi1 and one for Kpi2.
select empid,
case kpiname when 'Kpi1' then sum(kpivalue) end as 'Kpi1',
case kpiname when 'Kpi2' then sum(kpivalue) end as 'Kpi2'
from
[Kpis]
where kpiname in ('Kpi1', 'Kpi2')
and date = 20160310 and hour = 8
group by empid, kpiname, hour
How can I use the Case statement to fix the results?
Thanks.

Put the case inside your sum, such that you for each KpiName only sums the relevant values.
SELECT
EmpId,
[Hour],
SUM(
CASE
WHEN KpiName = 'Kpi1' THEN KpiValue
ELSE 0
END
) Kpi1,
SUM(
CASE
WHEN KpiName = 'Kpi2' THEN KpiValue
ELSE 0
END
) Kpi2
FROM
Kpis
GROUP BY
EmpId,
[Hour]
This produces this output
EmpId Hour Kpi1 Kpi2
ABC109 8 0 34
ABC123 8 122 12
ABC907 8 24 0
ABC908 9 3 0

SUM fucntion have to be outside of CASE:
select empid,
sum(case kpiname when 'Kpi1' then kpivalue end) as 'Kpi1',
sum(case kpiname when 'Kpi2' then kpivalue end) as 'Kpi2'
from
[Kpis]
where kpiname in ('Kpi1', 'Kpi2')
and date = 20160310 and hour = 8
group by empid, kpiname, hour

You can also do this with the PIVOT functionality, which I believe is what you're actually trying to accomplish.
SELECT
*
FROM (
SELECT
EmpId,
KpiName,
[Hour],
KpiValue
FROM
Kpis
) SourceTable
PIVOT (
SUM(KpiValue)
FOR KpiName
IN ([Kpi1],[Kpi2])
) PivotTable
Which gives this output. Note the NULLs as opposed to the zeros, correctly showing the lack of data.
EmpId Hour Kpi1 Kpi2
ABC109 8 NULL 34
ABC123 8 122 12
ABC907 8 24 NULL
ABC908 9 3 NULL

Related

How to show order fulfilment in a SQL Server 2008 query

I am trying to think of a way on a SQL Server 2008 database to run through a sales order table and get open demand for a part, order it by due date, then look at a purchase order table and fulfill the sales orders by PO, ordering the PO supply by due date as well. At the same time, I need to show what PO(s) are fulfilling the sales order.
For example:
SO table
SO# DueDate Part Number Required QTY
---------------------------------------------
100 9/3/16 1012 2
101 9/12/16 1012 1
107 10/11/16 1012 4
103 10/17/16 1012 7
PO table:
PO# DueDate Part Number Ordered QTY
--------------------------------------------
331 9/1/16 1012 1
362 9/2/16 1012 1
359 9/24/16 1012 5
371 10/1/16 1012 3
380 10/10/16 1012 10
With this data, I would like to see this result:
SO# DueDate Part Number Required QTY PO number QTY Used QTY Remain
--------------------------------------------------------------------------
100 9/3/16 1012 2 331 1 0
100 9/3/16 1012 1 362 1 0
101 9/12/16 1012 1 359 1 4
107 10/11/16 1012 4 359 4 0
103 10/17/16 1012 7 371 3 0
103 10/17/16 1012 7 380 4 6
I have done this sales order fulfillment process before, but not to the point of breaking down what PO(s) are fulfilling the order, only to the point of summing all open supply, then running through and subtracting the supply from each sales order to get a running balance of supply left.
Many thanks in advance for your help.
I found a bit weird solution, hope it helps you. Maybe later I could optimize it, but now I post it as is:
;WITH cte AS (
SELECT 1 as l
UNION ALL
SELECT l+1
FROM cte
WHERE l <= 1000000
), SO_cte AS (
SELECT *,
ROW_NUMBER() OVER (ORDER BY DueDate ASC) as rn
FROM SO s
CROSS JOIN cte c
WHERE c.l <= s.[Required QTY]
), PO_cte AS (
SELECT *,
ROW_NUMBER() OVER (ORDER BY DueDate ASC) as rn
FROM PO p
CROSS JOIN cte c
WHERE c.l <= p.[Ordered QTY]
), almost_done AS (
SELECT DISTINCT
s.SO#,
s.DueDate,
s.[Part Number],
p.PO#,
s.[Required QTY],
p.[Ordered QTY]
FROM SO_cte s
LEFT JOIN PO_cte p
ON p.rn = s.rn
), final AS (
SELECT *,
ROW_NUMBER() OVER (ORDER BY DueDate) AS RN
FROM almost_done
)
SELECT f.SO#,
f.DueDate,
f.[Part Number],
f.[Required QTY],
f.PO#,
CASE WHEN f.[Ordered QTY]>f.[Required QTY]
THEN ISNULL(ABS(f1.[Required QTY]-f1.[Ordered QTY]),f.[Required QTY])
ELSE f.[Ordered QTY] END
as [QTY Used],
f.[Ordered QTY] -
CASE WHEN f1.PO# = f.PO#
THEN f1.[Ordered QTY]
ELSE
CASE WHEN f.[Ordered QTY]>f.[Required QTY]
THEN ISNULL(ABS(f1.[Required QTY]-f1.[Ordered QTY]),f.[Required QTY])
ELSE f.[Ordered QTY] END
END as [QTY Remain]
FROM final f
LEFT JOIN final f1
ON f.RN = f1.RN+ 1
AND (f.SO# = f1.SO# OR f.PO# = f1.PO#)
OPTION(MAXRECURSION 0)
Output for data you provided:
SO# DueDate Part Number Required QTY PO# QTY Used QTY Remain
100 2016-09-03 1012 2 331 1 0
100 2016-09-03 1012 2 362 1 0
101 2016-09-12 1012 1 359 1 4
107 2016-10-11 1012 4 359 4 0
103 2016-10-17 1012 7 371 3 0
103 2016-10-17 1012 7 380 4 6

SQL, using Group by until specific trend (increment, decrement, same)

I would like to know how can i modify my code for considering all the same values of suppose 10 as UP till the time it is incrementing and then down for decrement and SAME if there is no change till the time there is no variation in the value (increment, decrement, same).
Here is my code :
;with etape1 as
(
select ROW_NUMBER() OVER(ORDER BY mnth) AS id,* from [InsideTSQL2008].[alioune].[Sales]
)
,
etape2 as
(
select
a.id, b.mnth AS START , a.mnth AS FINISH ,
a.qty - b.qty AS TREND
FROM
etape1 a
LEFT JOIN etape1 b
on a.id = b.id+1
)
select * from etape2;
My Result is :
id START FINISH TREND
1 NULL 2007-12-01 NULL
2 2007-12-01 2008-01-01 10
3 2008-01-01 2008-02-01 10
4 2008-02-01 2008-03-01 10
5 2008-03-01 2008-04-01 10
6 2008-04-01 2008-05-01 0
7 2008-05-01 2008-06-01 -10
8 2008-06-01 2008-07-01 -10
9 2008-07-01 2008-08-01 -10
10 2008-08-01 2008-09-01 -10
11 2008-09-01 2008-10-01 10
12 2008-10-01 2008-11-01 -10
13 2008-11-01 2008-12-01 20
14 2008-12-01 2009-01-01 10
15 2009-01-01 2009-02-01 10
16 2009-02-01 2009-03-01 -40
My final result as required should be like :
Start End Trend
200712 200712 unknown
200801 200804 UP
200805 200805 SAME
200806 200809 DOWN
200810 200810 UP
200811 200811 DOWN
200812 200812 UP
200903 200903 DOWN
200904 200905 SAME
200906 200907 UP
Any help would be really helpful; Thanks
Took me a few goes (and a few hours), but I think I have what you want:
DECLARE #Sales AS TABLE (mnth datetime, qty int)
INSERT INTO #Sales
SELECT '2016-01-01', 10 UNION ALL
SELECT '2016-02-01', 20 UNION ALL
SELECT '2016-03-01', 30 UNION ALL
SELECT '2016-04-01', 40 UNION ALL
SELECT '2016-05-01', 40 UNION ALL
SELECT '2016-06-01', 30 UNION ALL
SELECT '2016-07-01', 20 UNION ALL
SELECT '2016-08-01', 30 UNION ALL
SELECT '2016-09-01', 40 UNION ALL
SELECT '2016-10-01', 45 UNION ALL
SELECT '2016-11-01', 50
;WITH etape1 AS (
SELECT ROW_NUMBER() OVER(ORDER BY mnth) AS id, * FROM #Sales
)
, etape2 AS (
SELECT id, lag(mnth) OVER (ORDER BY id) AS START, mnth AS FINISH, CASE WHEN qty - LAG(qty) OVER (ORDER BY id) < 0 THEN -1 WHEN qty - LAG(qty) OVER (ORDER BY id) > 0 THEN 1 ELSE 0 END AS TREND
FROM etape1
)
, etape3 AS (
SELECT id, START, FINISH, TREND, lag(TREND) OVER (ORDER BY id) AS PrevTrend
FROM etape2
)
, etape4 AS (
SELECT id, START, FINISH, TREND, SUM(CASE WHEN TREND = PREVTREND THEN 0 ELSE 1 END) OVER (ORDER BY id ROWS UNBOUNDED PRECEDING) AS Change
FROM etape3
)
SELECT MIN(START) AS START, MAX(FINISH) AS FINISH, CASE WHEN MIN(TREND) IS NULL THEN 'Unknown' WHEN MIN(TREND) < 0 THEN 'Down' WHEN MIN(TREND) > 0 THEN 'Up' WHEN MIN(Start) is NULL THEN 'Unknown' ELSE 'Same' END AS TREND
FROM etape4
GROUP BY Change
ORDER BY START
Results are:
START FINISH TREND
NULL 2016-01-01 Unknown
2016-01-01 2016-04-01 Up
2016-04-01 2016-05-01 Same
2016-05-01 2016-07-01 Down
2016-07-01 2016-11-01 Up

Count field Pivot table In SQL Server

I have this table - Name : Mytable:
Amount Desc Month Sym code ID
$32,323.00 Bla1 1 121 3 2424221
$4,242.00 Bla1 1 121 3 2424221
$3,535.00 Bla1 1 121 1 3230824
$4,984.00 Bla2 1 433 1 3230824
$47,984.00 Bla2 2 433 1 3230824
$41.00 Bla2 2 433 1 3230824
$3,472.00 Bla6 1 D2 27 2297429
$3,472.00 Bla6 1 D2 27 2297429
$3,239.00 Bla6 2 D2 27 2297429
$4,249.00 Bla8 2 114 24 3434334
ID and Month Stands for for a paycheck. There are 6 paychecks : 1 + 3230824, 2+3230824 etc.
And I want to generate a pivot like this:
Jan Feb
count amount count amount
121 2 40100$ 0 0
433 1 52968$ 1 48025$
D2 1 6944$ 1 3239$
114 0 0 1 4249$
Explanation: 121 is two in Jan because ID = 2424221 got it twice and 3230824 got it one time. The number of of "occurrences" in the paychecks is two.
But, In the amount I sum every thing To get the total sum of money in the paycheck for that Sym.
Same, 433 got the value of 1 in Feb for example because only 3230824 got it (twice).
I started writing this:
SELECT *
FROM (
SELECT
[Sym] as Sym, [Month] as [month], [Amount] as Amount
FROM Mytable
) as T
PIVOT
(
Sum(Amount)
FOR [Month] IN ([1],[2])
)AS piv
Well, The amounts are correct But I don't know how can I pull this count as I explained near the amount in the pivot table.
SELECT
[Sym],
COALESCE(SUM(CASE WHEN [1] IS NULL THEN NULL ELSE [Cnt] END), 0) [Jan Count],
COALESCE(SUM(CASE WHEN [1] IS NULL THEN NULL ELSE [1] END), 0) [Jan Amount],
COALESCE(SUM(CASE WHEN [2] IS NULL THEN NULL ELSE [Cnt] END), 0) [Feb Count],
COALESCE(SUM(CASE WHEN [2] IS NULL THEN NULL ELSE [2] END), 0) [Feb Amount]
FROM (
SELECT
mt1.[Sym] as Sym, mt1.[Month] as [month], mt1.[Amount] as Amount, mt2.[Cnt]
FROM Mytable mt1
JOIN (SELECT COUNT(DISTINCT [ID]) [Cnt], [Sym], [Month]
FROM MyTable
GROUP BY [Sym], [Month]) mt2
ON mt1.[Sym] = mt2.[Sym] AND mt1.[Month] = mt2.[Month]
) as T
PIVOT
(
Sum(Amount)
FOR [Month] IN ([1],[2])
)AS piv
GROUP BY [Sym]
SQL Fiddle

Running total of records based on date ranges in t-sql

Sample data:
groupID CustomerID CustomerAddr work_date work_order
CA123 ABC12345 123 MAIN ST 2/1/2012 WORKNEW
CA123 ABC12345 123 MAIN ST 10/9/2012 ZZZ888
CA123 ABC12345 123 MAIN ST 3/9/2013 ZZZ131
WA999 ZZZ99909 451 EAST ST 1/13/2013 SY1234
WA999 ZZZ99909 451 EAST ST 4/15/2013 WORKOTHER
WA999 ZZZ99909 451 EAST ST 5/17/2013 SY1244
WA999 ZZZ99909 451 EAST ST 12/8/2013 SY1334
CA123 ABD54321 522 AVE A 4/21/2013 WW9999
For the given sample data, I need to count how many times records with the same groupID and CustomerID appear within 6, 12 and 18 month intervals. The count should be independent of other records with same groupID and customerID.
The output should look something like this:
groupID CustomerID work_date six_month_count twelve_month_count twenty_four_month_count
CA123 ABC12345 2/1/2012 0 1 2
CA123 ABC12345 10/9/2012 1 1 1
CA123 ABC12345 3/9/2013 0 0 0
WA999 ZZZ99909 1/13/2013 2 3 3
WA999 ZZZ99909 4/15/2013 1 2 2
WA999 ZZZ99909 5/17/2013 0 1 1
WA999 ZZZ99909 12/8/2013 0 0 0
CA123 ABD54321 4/21/2013 0 0 0
The only thing I can think to do is to grab the current record and max records for each row, but i don't know how to count the records for the given ranges.
Any help would be appreciated.
You can join the table to itself, and then work out the 6,12 and 24 columns via an aggregate. I've used Work_Order as a key to avoid counting the record vs itself during the self join.
WITH cteCountInRange
AS
(
SELECT c1.CustomerID,
c1.GroupID,
c1.Work_Date as WorkDate1,
c2.Work_Date as WorkDate2,
CASE WHEN c2.Work_Date > c1.Work_Date AND c1.Work_Order <> c2.Work_Order
THEN 1
ELSE 0
END AS Eligible,
DATEDIFF(M, c1.Work_Date, c2.Work_Date ) AS MonthsDiff
FROM CustomerData c1 INNER JOIN CustomerData c2
ON c1.CustomerID = c2.CustomerID AND c1.GroupID = c2.GroupID
)
SELECT cir.CustomerID, cir.GroupID, cir.WorkDate1,
SUM(CASE WHEN Eligible = 1 AND MonthsDiff <= 6 THEN 1 ELSE 0 END) AS SixMonthCount,
SUM(CASE WHEN Eligible = 1 AND MonthsDiff <= 12 THEN 1 ELSE 0 END) AS TwelveMonthCount,
SUM(CASE WHEN Eligible = 1 AND MonthsDiff <= 24 THEN 1 ELSE 0 END) AS TwentyFourMonthCount
FROM cteCountInRange cir
GROUP BY cir.CustomerID, cir.GroupID, cir.WorkDate1;
Fiddle here
Edit
Additional comment - the reason we can't filter out "InEligible" rows in the CTE is because a line of ZEROES is needed if there are no other matched rows after the current one. If we filtered these, we would need another LEFT JOIN back to the original table to include the zero rows.

Rolling up values in SQL Server

This is the result of my first sql statement:
SELECT
count(*) countQuarter, Hour, Quarter,
ROW_NUMBER() OVER(ORDER BY Hour, Quarter ASC) AS rownum
FROM
(SELECT [ID] ,[simulationID] ,[time],
replace(str(time/3600,len(ltrim(time/3600))+abs(sign(time/359999)-1)) + ':' + str((time/60)%60,2) + ':' + str(time%60,2),' ','0') dtString,
(time/3600) Hour, (time/60)%60 Minute, case when (time/60)%60<15 then 15 when
(time/60)%60<30 then 30 when (time/60)%60<45 then 45 when (time/60)%60<60 then 60 end
Quarter ,[person] ,[link] ,[vehicle] FROM [TEST].[dbo].[evtLinks]
WHERE simulationID=#simulationID) B
GROUP BY Hour, Quarter
which gives the following results:
Count Hour Quarter Rownum
497 0 15 1
842 0 30 2
1033 0 45 3
1120 0 60 4
1235 1 15 5
1267 1 30 6
1267 1 45 7
1267 1 60 8
1267 2 15 9
1267 2 30 10
I desire a result, where the column fullCount is the sum of the Count of the actual row and the next 3!
Count Hour Quarter Rownum Fullcount
497 0 15 1 3492
842 0 30 2 4230
1033 0 45 3 4655
1120 0 60 4 ...
1235 1 15 5
1267 1 30 6
1267 1 45 7
1267 1 60 8
1267 2 15 9
1267 2 30 10
How can this be done with grouping or analytical functions in SQL Server?
For SQL Server 2012, yes this can be done:
declare #t table ([Count] int,[Hour] int,[Quarter] int,Rownum int)
insert into #t([Count],[Hour],[Quarter],Rownum) values
(497 , 0 , 15 , 1 ),
(842 , 0 , 30 , 2 ),
(1033 , 0 , 45 , 3 ),
(1120 , 0 , 60 , 4 ),
(1235 , 1 , 15 , 5 ),
(1267 , 1 , 30 , 6 ),
(1267 , 1 , 45 , 7 ),
(1267 , 1 , 60 , 8 ),
(1267 , 2 , 15 , 9 ),
(1267 , 2 , 30 , 10 )
select *,SUM([Count]) OVER (
ORDER BY rownum
ROWS BETWEEN CURRENT ROW AND
3 FOLLOWING)
from #t
Here I'm using #t as your current result set - you may be able to adapt this into your current query or may have to place your current query in a CTE.
Unfortunately, the ROWS BETWEEN syntax is only valid on 2012 and later.
Tested the logical scenario and it works, but I don't have your data, so in your case it should look roughly like this:
;WITH CTE as (SELECT count(*) countQuarter,Hour,Quarter,
ROW_NUMBER() OVER(ORDER BY Hour, Quarter ASC) AS rownum
FROM
(SELECT [ID] ,[simulationID] ,[time],
replace(str(time/3600,len(ltrim(time/3600))+abs(sign(time/359999)-1)) + ':' + str((time/60)%60,2) + ':' + str(time%60,2),' ','0') dtString,
(time/3600) Hour, (time/60)%60 Minute, case when (time/60)%60<15 then 15 when
(time/60)%60<30 then 30 when (time/60)%60<45 then 45 when (time/60)%60<60 then 60 end
Quarter ,[person] ,[link] ,[vehicle] FROM [TEST].[dbo].[evtLinks]
WHERE simulationID=#simulationID) B
GROUP BY Hour, Quarter)
SELECT *, CA.Fullcount
FROM CTE
CROSS APPLY (SELECT SUM(countQuarter) Fullcount FROM CTE C WHERE C.ID BETWEEN CTE.ID AND CTE.ID+3) CA

Resources