SQL Server add all rows where a condition is validate - sql-server

I have a SQL Server database in which I need to add all the cost for a job family.
I have a table like this
Table : work
+-------+-----------+-----------+---------+
| wonum | cost1 | cost2 | wogroup |
+-------+-----------+-----------+---------+
| 1 | 30.12 | 157.14 | 1 |
| 2 | 110.10 | 0.00 | 1 |
| 3 | 12.67 | 45.45 | 1 |
| 4 | 0.00 | 0.00 | 4 |
| 5 | 400.00 | 11.54 | 4 |
+-------+-----------+-----------+---------+
I need to add cost1 and cost2 for all the row who have the same wogroup but only for the on where wonum = wogroup.
Like this
+-------+-----------+-----------+---------+---------+
| wonum | cost1 | cost2 | wogroup | total |
+-------+-----------+-----------+---------+---------+
| 1 | 30.12 | 157.14 | 1 | 355.48 |
| 2 | 110.10 | 0.00 | 1 | null |
| 3 | 12.67 | 45.45 | 1 | null |
| 4 | 0.00 | 0.00 | 4 | 411.54 |
| 5 | 400.00 | 11.54 | 4 | null |
+-------+-----------+-----------+---------+---------+
In a perfect world, the null value would be the sum of cost1 and cost2 for the row but I'm not sure if it is possible...
EDIT: I can only do a select, it is for a BiRT report

Since this can change with more wonum being added, I'd have this as a VIEW
declare #work table (wonum int , cost1 decimal (6,3), cost2 decimal (6,3) , wogroup int)
insert into #work
values
(1,30.12,157.14,1),
(2,110.10,0.00,1),
(3,12.67,45.45,1),
(4,0.00,0.00,4),
(5,400.00,11.54,4)
select
*,
total = case when wonum = min(wonum) over (partition by wogroup) then sum(cost1) over (partition by wogroup) + sum(cost2) over (partition by wogroup) end
from #work
RETURNS
+-------+-----------+-----------+---------+---------+
| wonum | cost1 | cost2 | wogroup | total |
+-------+-----------+-----------+---------+---------+
| 1 | 30.12 | 157.14 | 1 | 355.48 |
| 2 | 110.10 | 0.00 | 1 | null |
| 3 | 12.67 | 45.45 | 1 | null |
| 4 | 0.00 | 0.00 | 4 | 411.54 |
| 5 | 400.00 | 11.54 | 4 | null |
+-------+-----------+-----------+---------+---------+
YOUR QUERY
select
*,
total = case when wonum = min(wonum) over (partition by wogroup)
then sum(cost1) over (partition by wogroup) + sum(cost2) over (partition by wogroup)
else null
end
from work

Related

SQL Server Lag by partitioned group

I have a table of data as follows:
+----+-------+----------+
| id | value | group_id |
+----+-------+----------+
| 1 | -200 | 0 |
| 2 | -620 | 0 |
| 3 | -310 | 0 |
| 4 | 400 | 1 |
| 5 | 300 | 1 |
| 6 | 100 | 1 |
| 7 | -200 | 2 |
| 8 | -400 | 2 |
| 9 | -500 | 2 |
+----+-------+----------+
What I would like to do is produce a 4th column that, for each record, shows the last value of the preceding group_id.
So the result I want is as follows:
+----+-------+----------+----------------+
| id | value | group_id | LastValByGroup |
+----+-------+----------+----------------+
| 1 | -200 | 0 | 0 |
| 2 | -620 | 0 | 0 |
| 3 | -310 | 0 | 0 |
| 4 | 400 | 1 | -310 |
| 5 | 300 | 1 | -310 |
| 6 | 100 | 1 | -310 |
| 7 | -200 | 2 | 100 |
| 8 | -400 | 2 | 100 |
| 9 | -500 | 2 | 100 |
+----+-------+----------+----------------+
What I have done so far is in 2 parts. First I use the LAST_VALUE function to get the last Value in each group. Then I have tried to use the LAG function to get the last value from the previous group. Unfortunately the second part of my code isn't working as desired.
Here is my code:
CREATE TABLE #temp
(
id int identity(1,1),
value int,
group_id int
)
INSERT #temp VALUES(-200,0)
INSERT #temp VALUES(-620,0)
INSERT #temp VALUES(-310,0)
INSERT #temp VALUES(400,1)
INSERT #temp VALUES(300,1)
INSERT #temp VALUES(100,1)
INSERT #temp VALUES(-200,3)
INSERT #temp VALUES(-400,3)
INSERT #temp VALUES(-500,3)
;WITH cte AS
(
SELECT
*,
LastValByGroup = LAST_VALUE(Value) OVER(Partition By group_id ORDER BY id
RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
FROM
#temp
), lagged AS
(
SELECT
*,
LaggedLastValByGroup = LAG(LastValByGroup,1,0) OVER(Partition By group_id ORDER BY id)
FROM
cte
)
SELECT * FROM lagged ORDER BY id
DROP TABLE #temp
And this is the result I get:
+----+-------+----------+----------------+----------------------+
| id | value | group_id | LastValByGroup | LaggedLastValByGroup |
+----+-------+----------+----------------+----------------------+
| 1 | -200 | 0 | -310 | 0 |
| 2 | -620 | 0 | -310 | -310 |
| 3 | -310 | 0 | -310 | -310 |
| 4 | 400 | 1 | 100 | 0 |
| 5 | 300 | 1 | 100 | 100 |
| 6 | 100 | 1 | 100 | 100 |
| 7 | -200 | 3 | -500 | 0 |
| 8 | -400 | 3 | -500 | -500 |
| 9 | -500 | 3 | -500 | -500 |
+----+-------+----------+----------------+----------------------+
Any help is much appreciated.
Thanks
You can use first_value like following to get the desired result.
select distinct t2.*, ISNULL(FIRST_VALUE(t1.[value]) over(partition by t1.group_id order by t1.id desc), 0) LastValByGroup
from #data t1
right join #data t2 on t1.group_id + 1 = t2.group_id
Please find the db<>fiddle here.

SQL server multi-period comparison

I have the following table T1 (sample shown), which shows the category for each client (each with a unique ID) on a specific date and his category on the next date:
+------------+----------------+----------+---------------+
| DATE | ID | STAGE | STAGE_NEXT |
+------------+----------------+----------+---------------+
| 2014-07-01 | 10010101841033 | 1 | 1 |
| 2015-07-01 | 74610108542146 | 1 | 1 |
| 2014-10-01 | 47970108841775 | 3 | 3 |
| 2014-10-01 | 48870108841816 | 2 | 3 |
| 2014-10-01 | 32910097439541 | 1 | 1 |
| 2016-04-01 | 46930097440855 | 2 | 3 |
| 2016-04-01 | 47380097440931 | 2 | 3 |
| 2016-04-01 | 54560097441411 | 3 | 3 |
+------------+----------------+----------+---------------+
Table info:
- Rows: 513,000
- Date range: 2013-01-01 to 2019-10-01
- Stages: 1 - 3
I need to create a new column in T1, which will flag the date a client moved to Stage 1 if at any point he was in Stage 3. For example if we take 1 client from T1 by using this code:
SELECT [DATE], ID, STAGE, STAGE_NEXT
FROM T1
WHERE ID = '74610108542146'
ORDER BY [DATE]
We get the following result:
+------------+----------------+-------+------------+
| DATE | ID | STAGE | STAGE_NEXT |
+------------+----------------+-------+------------+
| 2015-07-01 | 74610108542146 | 1 | 1 |
| 2015-10-01 | 74610108542146 | 1 | 1 |
| 2016-01-01 | 74610108542146 | 1 | 2 |
| 2016-04-01 | 74610108542146 | 2 | 1 |
| 2016-07-01 | 74610108542146 | 1 | 1 |
| 2016-10-01 | 74610108542146 | 1 | 2 |
| 2017-01-01 | 74610108542146 | 2 | 3 |
| 2017-04-01 | 74610108542146 | 3 | 3 |
| 2017-07-01 | 74610108542146 | 3 | 2 |
| 2017-10-01 | 74610108542146 | 2 | 1 |
| 2018-01-01 | 74610108542146 | 1 | 1 |
| 2018-04-01 | 74610108542146 | 1 | NULL |
+------------+----------------+-------+------------+
After the new column with the flag is added to T1 we should be able to get the following result using this code on T1:
SELECT [DATE], ID, STAGE, STAGE_NEXT, FLAG
FROM T1
WHERE ID = '74610108542146'
ORDER BY [DATE]
+------------+----------------+-------+------------+------+
| DATE | ID | STAGE | STAGE_NEXT | FLAG |
+------------+----------------+-------+------------+------+
| 2015-07-01 | 74610108542146 | 1 | 1 | 0 |
| 2015-10-01 | 74610108542146 | 1 | 1 | 0 |
| 2016-01-01 | 74610108542146 | 1 | 2 | 0 |
| 2016-04-01 | 74610108542146 | 2 | 1 | 0 |
| 2016-07-01 | 74610108542146 | 1 | 1 | 0 |
| 2016-10-01 | 74610108542146 | 1 | 2 | 0 |
| 2017-01-01 | 74610108542146 | 2 | 3 | 0 |
| 2017-04-01 | 74610108542146 | 3 | 3 | 0 |
| 2017-07-01 | 74610108542146 | 3 | 2 | 0 |
| 2017-10-01 | 74610108542146 | 2 | 1 | 1 |
| 2018-01-01 | 74610108542146 | 1 | 1 | 0 |
| 2018-04-01 | 74610108542146 | 1 | NULL | 0 |
+------------+----------------+-------+------------+------+
If the client never moved to Stage 3 then the flag for the client is always 0
You could calculate and update the new FLAG column from a CTE.
The update statement uses the LAG function to use the previous STAGE in the calculation of FLAG.
;WITH CTE AS
(
SELECT ID, [DATE], FLAG,
CASE
WHEN STAGE = 2
AND STAGE_NEXT = 1
AND LAG(STAGE) OVER (PARTITION BY ID ORDER BY IIF(STAGE=2 AND STAGE_NEXT=2,0,1), [DATE]) = 3
THEN 1
ELSE 0
END AS CalcFlag
FROM T1
WHERE ID = '10010101841033' -- optional, to target only 1 ID
)
UPDATE CTE
SET FLAG = CalcFlag
WHERE (FLAG IS NULL OR FLAG != CalcFlag);
The IIF(STAGE=2 AND STAGE_NEXT=2,0,1) in the LAG is used to make the calculation also work when the stage 2 is repeated.
Test it on rextester here
Try this,
DECLARE #T1 table
(
[DATE] date,ID numeric(18,0),STAGE int,STAGE_NEXT int
)
INSERT INTO #T1 VALUES
('2013-01-01',10010101841033,1,1 ),
('2013-04-01',10010101841033,1,3 ),
('2013-07-01',10010101841033,3,3 ),
('2013-10-01',10010101841033,3,2 ),
('2014-01-01',10010101841033,2,1 ),
('2014-04-01',10010101841033,1,1 ),
('2014-07-01',10010101841033,1,1 ),
('2014-10-01',10010101841033,1,NULL),
('2014-07-01',47820108841771,1,2)
SELECT A.DATE,A.ID,A.STAGE,A.STAGE_NEXT,
CASE WHEN B.ID IS NOT NULL AND (STAGE_NEXT=1 AND STAGE>STAGE_NEXT) THEN 1 ELSE 0 END AS FLAG
FROM #T1 A
LEFT JOIN
(
SELECT DISTINCT ID AS ID
FROM #T1
WHERE STAGE_NEXT=3
)B
ON A.ID=B.ID

T-SQL: Values are grouped by month, if there is no value for a month the month should also appear and display "NULL"

i have a SQL that displays turnover, stock and other values for stores grouped by month. Logically, if there is no value for a month, the month doesn't appear. The target is that the empty month should appear and display "NULL" for the values. The empty months should range from the #FROM to the #TO parameter (201807 to 201907) in this case.
Before:
+-------+--------+----------+----------+-------+
| Store | Month | Incoming | Turnover | Stock |
+-------+--------+----------+----------+-------+
| 123 | 201810 | 5 | 4 | 1 |
| 123 | 201811 | 0 | 1 | 0 |
| 123 | 201901 | 25 | 5 | 20 |
| 123 | 201902 | 5 | 10 | 15 |
| 123 | 201903 | 8 | 9 | 14 |
| 123 | 201904 | 5 | 4 | 15 |
| 123 | 201905 | 10 | 5 | 20 |
+-------+--------+----------+----------+-------+
After:
+-------+--------+----------+----------+-------+
| Store | Month | Incoming | Turnover | Stock |
+-------+--------+----------+----------+-------+
| 123 | 201807 | NULL | NULL | NULL |
| 123 | 201808 | NULL | NULL | NULL |
| 123 | 201809 | NULL | NULL | NULL |
| 123 | 201810 | 5 | 4 | 1 |
| 123 | 201811 | 0 | 1 | 0 |
| 123 | 201812 | NULL | NULL | NULL |
| 123 | 201901 | 25 | 5 | 20 |
| 123 | 201902 | 5 | 10 | 15 |
| 123 | 201903 | 8 | 9 | 14 |
| 123 | 201904 | 5 | 4 | 15 |
| 123 | 201905 | 10 | 5 | 20 |
| 123 | 201906 | NULL | NULL | NULL |
| 123 | 201907 | NULL | NULL | NULL |
+-------+--------+----------+----------+-------+
Code Example: db<>fiddle
I have absolutely no idea how to solve this and will thank you in advance for your help! :)
You can try to use cte recursive make a calendar table, then do outer-join
;WITH CTE AS (
SELECT CAST(CAST(#FROM AS VARCHAR(10)) + '01' AS DATE) fromDt,
CAST(CAST(#TO AS VARCHAR(10)) + '01' AS DATE) toDt,
Store
FROM (SELECT DISTINCT Store FROM #Test) t1
UNION ALL
SELECT DATEADD(MONTH,1,fromDt),toDt,Store
FROM CTE
WHERE DATEADD(MONTH,1,fromDt) <= toDt
)
SELECT FORMAT(fromDt,'yyyyMM') Month,
c.Store,
t.Incoming,
t.Turnover,
t.Stock
FROM CTE c
LEFT JOIN #Test t on
c.fromDt = CAST(CAST(t.Month AS VARCHAR(10)) + '01' AS DATE)
and
c.Store = t.Store
sqlfiddle

pivot and cascade null columns

I have a table that holds values for particular months:
| MFG | DATE | FACTOR |
-----------------------------
| 1 | 2013-01-01 | 1 |
| 2 | 2013-01-01 | 0.8 |
| 2 | 2013-02-01 | 1 |
| 2 | 2013-12-01 | 1.55 |
| 3 | 2013-01-01 | 1 |
| 3 | 2013-04-01 | 1.3 |
| 3 | 2013-05-01 | 1.2 |
| 3 | 2013-06-01 | 1.1 |
| 3 | 2013-07-01 | 1 |
| 4 | 2013-01-01 | 0.9 |
| 4 | 2013-02-01 | 1 |
| 4 | 2013-12-01 | 1.8 |
| 5 | 2013-01-01 | 1.4 |
| 5 | 2013-02-01 | 1 |
| 5 | 2013-10-01 | 1.3 |
| 5 | 2013-11-01 | 1.2 |
| 5 | 2013-12-01 | 1.5 |
What I would like to do is pivot these using a calendar table (already defined):
And finally, cascade the NULL columns to use the previous value.
What I've got so far is a query that will populate the NULLs with the last value for mfg = 3. Each mfg will always have a value for the first of the year. My question is; how do I pivot this and extend to all mfg?
SELECT c.[date],
f.[factor],
Isnull(f.[factor], (SELECT TOP 1 factor
FROM factors
WHERE [date] < c.[date]
AND [factor] IS NOT NULL
AND mfg = 3
ORDER BY [date] DESC)) AS xFactor
FROM (SELECT [date]
FROM calendar
WHERE Datepart(yy, [date]) = 2013
AND Datepart(d, [date]) = 1) c
LEFT JOIN (SELECT [date],
[factor]
FROM factors
WHERE mfg = 3) f
ON f.[date] = c.[date]
Result
| DATE | FACTOR | XFACTOR |
---------------------------------
| 2013-01-01 | 1 | 1 |
| 2013-02-01 | (null) | 1 |
| 2013-03-01 | (null) | 1 |
| 2013-04-01 | 1.3 | 1.3 |
| 2013-05-01 | 1.2 | 1.2 |
| 2013-06-01 | 1.1 | 1.1 |
| 2013-07-01 | 1 | 1 |
| 2013-08-01 | (null) | 1 |
| 2013-09-01 | (null) | 1 |
| 2013-10-01 | (null) | 1 |
| 2013-11-01 | (null) | 1 |
| 2013-12-01 | (null) | 1 |
SQL Fiddle
Don't know if you need the dates to be dynamic from the calender table or if mfg can be more than 5 but this should give you some ideas.
select *
from (
select c.date,
t.mfg,
(
select top 1 f.factor
from factors as f
where f.date <= c.date and
f.mfg = t.mfg and
f.factor is not null
order by f.date desc
) as factor
from calendar as c
cross apply(values(1),(2),(3),(4),(5)) as t(mfg)
) as t
pivot (
max(t.factor) for t.date in ([20130101], [20130201], [20130301],
[20130401], [20130501], [20130601],
[20130701], [20130801], [20130901],
[20131001], [20131101], [20131201])
) as P
SQL Fiddle

Multifilter SQL Server Pivot Count Query to table

I have never used SQL Pivot before and need help
I have the following data table (wkYield) in MS SQL Server 2012 which looks like:
| id | Trav_num | Part_num | Reason_code | Scrap | date |
| 1 | 123123 | 400 | cw_iweld | 1 | 1/1/2015 |
| 2 | 123122 | 400 | cw_iweld | 1 | 1/1/2015 |
| 3 | 123124 | 400 | cw_iweld | 0 | 1/7/2015 |
| 4 | 123124 | 400 | cw_iweld | 1 | 1/7/2015 |
| 5 | 123121 | 400 | cw_hole | 0 | 1/1/2015 |
| 6 | 123121 | 400 | cw_hole | 1 | 1/1/2015 |
| 7 | 123110 | 400 | cw_hole | 0 | 1/7/2015 |
| 8 | 123110 | 400 | cw_hole | 1 | 1/7/2015 |
| 9 | 123111 | 410 | cw_iweld | 0 | 1/1/2015 |
| 10 | 123111 | 410 | cw_iweld | 1 | 1/1/2015 |
| 11 | 123333 | 410 | cw_iweld | 1 | 1/1/2015 |
I would like to use SQL to pivot the data to count the # of rows and display like the following:
| Part_num | Reason_code | Week | Scrap=1 Cnt(reason)| Scrap=0 Cnt(reason)|
| 400 | cw_weld | 1 | 2 | 1 |
| 400 | cw_hole | 1 | 1 | 1 |
| 400 | cw_weld | 2 | 1 | 1 |
| 400 | cw_hole | 2 | 1 | 1 |
| 410 | cw_iweld | 1 | 2 | 1 |
And then the result should be placed in table wkYieldSum
I don't know for any given week number what the reason codes are (They change week to week but do have a lot of repeats.
All your help is very appreciated!
You can do this two ways one is conditional Aggregate another way is Pivot. I prefer Conditional Aggregate which more readable in my opinion
select Part_num,
Reason_code,
datepart(Week,[date]) as [Week],
count(case when Scrap=1 then 1 end) as [Scrap=1 Cnt(reason)],
count(case when Scrap=0 then 1 end) as [Scrap=0 Cnt(reason)],
From Yourtable
Group by Part_num,
Reason_code,
datepart(Week,[date])

Resources