Sum, Group by and Null - sql-server

I'm dipping my toes into SQL. I have the following table
+------+----+------+------+-------+
| Type | ID | QTY | Rate | Name |
+------+----+------+------+-------+
| B | 1 | 1000 | 21 | Jack |
| B | 2 | 2000 | 12 | Kevin |
| B | 1 | 3000 | 24 | Jack |
| B | 1 | 1000 | 23 | Jack |
| B | 3 | 200 | 13 | Mary |
| B | 2 | 3000 | 12 | Kevin |
| B | 4 | 4000 | 44 | Chris |
| B | 4 | 5000 | 43 | Chris |
| B | 3 | 1000 | 26 | Mary |
+------+----+------+------+-------+
I don't know how I would leverage Sum and Group by to achieve the following result.
+------+----+------+------+-------+------------+
| Type | ID | QTY | Rate | Name | Sum of QTY |
+------+----+------+------+-------+------------+
| B | 1 | 1000 | 21 | Jack | 5000 |
| B | 1 | 3000 | 24 | Jack | Null |
| B | 1 | 1000 | 23 | Jack | Null |
| B | 2 | 3000 | 12 | Kevin | 5000 |
| B | 2 | 3000 | 12 | Kevin | Null |
| B | 3 | 200 | 13 | Mary | 1200 |
| B | 3 | 1000 | 26 | Mary | Null |
| B | 4 | 4000 | 44 | Chris | 9000 |
| B | 4 | 5000 | 43 | Chris | Null |
+------+----+------+------+-------+------------+
Any help is appreciated!

You can use window function :
select t.*,
(case when row_number() over (partition by type, id order by name) = 1
then sum(qty) over (partition by type, id order by name)
end) as Sum_of_QTY
from table t;

Related

Trying to create a parent-child hierarchy in sql server

I have a data set that is sorted by account key. when coming to certain rows which have NULL as group key, it should add current account key as a parent key for all above rows which have NULL as parentkey. So when coming to the next row with null as group key, you would set the current account key as parent key for all rows above that have parent key like null.
I have tried to copy a dataset below as mark down table but as you see I can't say I succeeded very well, but I hope some of you can help with the t-sql syntax to create a parent-child hierarchy of this
| AccountKey | ParentKey | GroupKey | AccountNumber | Cat | LineName | LineId |
|------------|-----------|----------|---------------|----------|------------------------------|--------------------------------------|
| 1 | NULL | 7 | 3040 | Account | Salg fisk | C6BCDFB2-1AAC-4D05-94F1-879CDC615D76 |
| 2 | NULL | 7 | 3041 | Account | Salg fisk | C6BCDFB2-1AAC-4D05-94F1-879CDC615D76 |
| 3 | NULL | 7 | 3081 | Account | Salg fisk | C6BCDFB2-1AAC-4D05-94F1-879CDC615D76 |
| 4 | NULL | 7 | 3082 | Account | Salg fisk | C6BCDFB2-1AAC-4D05-94F1-879CDC615D76 |
| 5 | NULL | 7 | 3083 | Account | Salg fisk | C6BCDFB2-1AAC-4D05-94F1-879CDC615D76 |
| 6 | NULL | 7 | 3085 | Account | Salg fisk | C6BCDFB2-1AAC-4D05-94F1-879CDC615D76 |
| 7 | NULL | 7 | 3086 | Account | Salg fisk | C6BCDFB2-1AAC-4D05-94F1-879CDC615D76 |
| 8 | NULL | 7 | 3087 | Account | Salg fisk | C6BCDFB2-1AAC-4D05-94F1-879CDC615D76 |
| 9 | NULL | 2 | 3000 | Account | Salg annet | 26AC86B2-0667-463E-B994-11A5C6D519A6 |
| 10 | NULL | 2 | 3010 | Account | Salg annet | 26AC86B2-0667-463E-B994-11A5C6D519A6 |
| 11 | NULL | 2 | 3020 | Account | Salg annet | 26AC86B2-0667-463E-B994-11A5C6D519A6 |
| 12 | NULL | 2 | 3030 | Account | Salg annet | 26AC86B2-0667-463E-B994-11A5C6D519A6 |
| 41 | NULL | 11 | 3050 | Account | Andre driftsinntekter | 65FFB620-AE42-4BE5-A6E7-BF3339AA04DF |
| 42 | NULL | 11 | 3600 | Account | Andre driftsinntekter | 65FFB620-AE42-4BE5-A6E7-BF3339AA04DF |
| 43 | NULL | 11 | 3601 | Account | Andre driftsinntekter | 65FFB620-AE42-4BE5-A6E7-BF3339AA04DF |
| 44 | NULL | 11 | 3610 | Account | Andre driftsinntekter | 65FFB620-AE42-4BE5-A6E7-BF3339AA04DF |
| 45 | NULL | 11 | 3615 | Account | Andre driftsinntekter | 65FFB620-AE42-4BE5-A6E7-BF3339AA04DF |
| 46 | NULL | 11 | 3690 | Account | Andre driftsinntekter | 65FFB620-AE42-4BE5-A6E7-BF3339AA04DF |
| 47 | NULL | 11 | 3691 | Account | Andre driftsinntekter | 65FFB620-AE42-4BE5-A6E7-BF3339AA04DF |
| 48 | NULL | 11 | 3701 | Account | Andre driftsinntekter | 65FFB620-AE42-4BE5-A6E7-BF3339AA04DF |
| 49 | NULL | 11 | 3705 | Account | Andre driftsinntekter | 65FFB620-AE42-4BE5-A6E7-BF3339AA04DF |
| 50 | NULL | 11 | 3720 | Account | Andre driftsinntekter | 65FFB620-AE42-4BE5-A6E7-BF3339AA04DF |
| 67 | NULL | NULL | NULL | SubTotal | Sum inntekter | NULL |
| 68 | NULL | 13 | 4120 | Account | Innkjøp smolt/settefisk/rogn | F9EE1CE4-22C7-400B-BC9D-E2D3214A5113 |
| 69 | NULL | 10 | 4010 | Account | Vareforbruk fôr | 04E63B6D-CA54-423D-8A44-A4ED99861975 |
| 70 | NULL | 10 | 4901 | Account | Vareforbruk fôr | 04E63B6D-CA54-423D-8A44-A4ED99861975 |
| 71 | NULL | 3 | 4000 | Account | Andre varekostnader | DB7FABAB-7ABA-4B9A-9720-1B538D99B3C8 |
| 72 | NULL | 3 | 4020 | Account | Andre varekostnader | DB7FABAB-7ABA-4B9A-9720-1B538D99B3C8 |
| 73 | NULL | 3 | 4030 | Account | Andre varekostnader | DB7FABAB-7ABA-4B9A-9720-1B538D99B3C8 |
| 133 | NULL | 8 | 4925 | Account | Beholdningsendring fisk | A8BA6F19-A792-44A1-AA21-8F79DB24D224 |
| 134 | NULL | NULL | NULL | SubTotal | Sum varekostnader | NULL |
| 135 | NULL | 12 | 5000 | Account | Lønn og sosiale kostnader | 5C475EDE-3731-4D39-B11A-C8EE72213FF6 |
| 136 | NULL | 12 | 5001 | Account | Lønn og sosiale kostnader | 5C475EDE-3731-4D39-B11A-C8EE72213FF6 |
| 137 | NULL | 12 | 5005 | Account | Lønn og sosiale kostnader | 5C475EDE-3731-4D39-B11A-C8EE72213FF6 |
| 138 | NULL | 12 | 5009 | Account | Lønn og sosiale kostnader | 5C475EDE-3731-4D39-B11A-C8EE72213FF6 |
| 263 | NULL | NULL | NULL | SubTotal | Sum lønnskostnadern | NULL |
| 462 | NULL | NULL | NULL | SubTotal | RESULTAT ETTER SKATT | NULL
If I understood your question you could use:
SELECT Accountkey, ParentKey,GroupKey,AccountNumber, NEW_PARID
FROM (
SELECT Accountkey, ParentKey,GroupKey,AccountNumber, AccountKey AS NEW_PARID, LAG(ACCOUNTKEY) OVER (ORDER BY Accountkey) AS PREC
FROM MYT
WHERE GroupKey IS NULL
UNION ALL
SELECT A.Accountkey, A.ParentKey,A.GroupKey,A.AccountNumber, B.Accountkey AS NEW_PARID, B.PREC
FROM MYT A
INNER JOIN ( SELECT Accountkey, ParentKey,GroupKey,AccountNumber, AccountKey AS NEW_PARID, LAG(ACCOUNTKEY) OVER (ORDER BY Accountkey) AS PREC
FROM MYT
WHERE GroupKey IS NULL) B ON A.Accountkey < B.Accountkey AND (B.PREC IS NULL OR B.PREC<A.accountKey)
WHERE A.GroupKey IS NOT NULL
AND B.GroupKey IS NULL
) X ORDER BY ACCOUNTKEY
You can write it in this way too (it's the same query):
WITH X AS (SELECT Accountkey, ParentKey,GroupKey,AccountNumber, AccountKey AS NEW_PARID, LAG(ACCOUNTKEY) OVER (ORDER BY Accountkey) AS PREC
FROM MYT
WHERE GroupKey IS NULL)
SELECT X.*
FROM X
UNION ALL
SELECT A.Accountkey, A.ParentKey,A.GroupKey,A.AccountNumber, X.Accountkey AS NEW_PARID, X.PREC
FROM MYT A
INNER JOIN X ON A.Accountkey < X.Accountkey AND (X.PREC IS NULL OR X.PREC<A.accountKey)
WHERE A.GroupKey IS NOT NULL
Output (MYT is the name of the table, the new parentid column is NEW_PARID):
+------------+-----------+----------+---------------+-----------+
| Accountkey | ParentKey | GroupKey | AccountNumber | NEW_PARID |
+------------+-----------+----------+---------------+-----------+
| 1 | NULL | 7 | 3040 | 67 |
| 2 | NULL | 7 | 3041 | 67 |
| 3 | NULL | 7 | 3081 | 67 |
| 4 | NULL | 7 | 3082 | 67 |
| 5 | NULL | 7 | 3083 | 67 |
| 6 | NULL | 7 | 3085 | 67 |
| 7 | NULL | 7 | 3086 | 67 |
| 8 | NULL | 7 | 3087 | 67 |
| 9 | NULL | 2 | 3000 | 67 |
| 10 | NULL | 2 | 3010 | 67 |
| 11 | NULL | 2 | 3020 | 67 |
| 12 | NULL | 2 | 3030 | 67 |
| 41 | NULL | 11 | 3050 | 67 |
| 42 | NULL | 11 | 3600 | 67 |
| 43 | NULL | 11 | 3601 | 67 |
| 44 | NULL | 11 | 3610 | 67 |
| 45 | NULL | 11 | 3615 | 67 |
| 46 | NULL | 11 | 3690 | 67 |
| 47 | NULL | 11 | 3691 | 67 |
| 48 | NULL | 11 | 3701 | 67 |
| 49 | NULL | 11 | 3705 | 67 |
| 50 | NULL | 11 | 3720 | 67 |
| 67 | NULL | NULL | NULL | 67 |
| 68 | NULL | 13 | 4120 | 134 |
| 69 | NULL | 10 | 4010 | 134 |
| 70 | NULL | 10 | 4901 | 134 |
| 71 | NULL | 3 | 4000 | 134 |
| 72 | NULL | 3 | 4020 | 134 |
| 73 | NULL | 3 | 4030 | 134 |
| 133 | NULL | 8 | 4925 | 134 |
| 134 | NULL | NULL | NULL | 134 |
| 135 | NULL | 12 | 5000 | 263 |
| 136 | NULL | 12 | 5001 | 263 |
| 137 | NULL | 12 | 5005 | 263 |
| 138 | NULL | 12 | 5009 | 263 |
| 263 | NULL | NULL | NULL | 263 |
| 462 | NULL | NULL | NULL | 462 |
+------------+-----------+----------+---------------+-----------+
Updated 20171221 - for MSSQL 2008
You can try this (but pay attention for performance if you have a large dataset):
SELECT A.ACCOUNTKEY
, A.PARENTKEY
, (SELECT MIN(B.ACCOUNTKEY) FROM MYT B WHERE B.GROUPKEY IS NULL AND A.ACCOUNTKEY<=B.ACCOUNTKEY) AS NEW_PARID
FROM MYT A
/* WHERE A.GROUPKEY IS NOT NULL*/
This is the code I need to rewrite to work on SQL Server 2008
SELECT AccountKey,
LineName,
AccountName,
GroupKey,
AccountNumber,
ParentAccountKey
INTO tempAccount
FROM
(
SELECT AccountKey,
LineName,
AccountName,
GroupKey,
AccountNumber,
AccountKey AS ParentAccountKey,
LAG(AccountKey) OVER(ORDER BY AccountKey) AS PREC
FROM tempTable2
WHERE GroupKey IS NULL
UNION ALL
SELECT A.AccountKey,
A.LineName,
A.AccountName,
A.GroupKey,
A.AccountNumber,
B.AccountKey AS ParentAccountKey,
B.PREC
FROM tempTable2 A
INNER JOIN
(
SELECT AccountKey,
LineName,
AccountName,
GroupKey,
AccountNumber,
AccountKey AS ParentAccountKey,
LAG(AccountKey) OVER(ORDER BY AccountKey) AS PREC
FROM tempTable2
WHERE GroupKey IS NULL
) B ON A.AccountKey < B.AccountKey
AND (B.PREC IS NULL
OR B.PREC < A.AccountKey)
WHERE A.GroupKey IS NOT NULL
AND B.GroupKey IS NULL
) X
ORDER BY AccountKey;

Need to update "orderby" column

I have a table test
+----+--+------+--+--+----------+--+--------------+
| ID | | Name | | | orderby | | processgroup |
+----+--+------+--+--+----------+--+--------------+
| 1 | | ABC | | | 10 | | 1 |
| 10 | | DEF | | | 12 | | 1 |
| 15 | | LMN | | | 1 | | 1 |
| 44 | | JKL | | | 4 | | 1 |
| 42 | | XYZ | | | 3 | | 2 |
+----+--+------+--+--+----------+--+--------------+
I want to update the orderby column in the sequence, I am expecting output like
+----+--+------+--+--+----------+--+--------------+
| ID | | Name | | | orderby | | processgroup |
+----+--+------+--+--+----------+--+--------------+
| 1 | | ABC | | | 1 | | 1 |
| 10 | | DEF | | | 2 | | 1 |
| 15 | | LMN | | | 3 | | 1 |
| 44 | | JKL | | | 4 | | 1 |
| 42 | | XYZ | | | 5 | | 1 |
+----+--+------+--+--+----------+--+--------------+
Logic behind this is when we have procesgroup as 1, orderby column should update as 1,2,3,4 and when procesgroup is 2 then update orderby as 5.
This might help you
;WITH CTE AS (
SELECT ROW_NUMBER() OVER (ORDER BY processgroup, ID ) AS SNO, ID FROM TABLE1
)
UPDATE TABLE1 SET TABLE1.orderby= CTE.SNO FROM CTE WHERE TABLE1.ID = CTE.ID

SQL Server : How to subtract values throughout the rows by using values in another column?

I have a table named stock and sales as below :
Stock Table :
+--------+----------+---------+
| Stk_ID | Stk_Name | Stk_Qty |
+--------+----------+---------+
| 1001 | A | 20 |
| 1002 | B | 50 |
+--------+----------+---------+
Sales Table :
+----------+------------+------------+-----------+
| Sales_ID | Sales_Date | Sales_Item | Sales_Qty |
+----------+------------+------------+-----------+
| 2001 | 2016-07-15 | A | 5 |
| 2002 | 2016-07-20 | B | 7 |
| 2003 | 2016-07-23 | A | 4 |
| 2004 | 2016-07-29 | A | 2 |
| 2005 | 2016-08-03 | B | 15 |
| 2006 | 2016-08-07 | B | 10 |
| 2007 | 2016-08-10 | A | 5 |
+----------+------------+------------+-----------+
With the table above, how can I find the available stock Ava_Stk for each stock after every sales?
Ava_Stk is expected to subtract Sales_Qty from Stk_Qty after every sales.
+----------+------------+------------+-----------+---------+
| Sales_ID | Sales_Date | Sales_Item | Sales_Qty | Ava_Stk |
+----------+------------+------------+-----------+---------+
| 2001 | 2016-07-15 | A | 5 | 15 |
| 2002 | 2016-07-20 | B | 7 | 43 |
| 2003 | 2016-07-23 | A | 4 | 11 |
| 2004 | 2016-07-29 | A | 2 | 9 |
| 2005 | 2016-08-03 | B | 15 | 28 |
| 2006 | 2016-08-07 | B | 10 | 18 |
| 2007 | 2016-08-10 | A | 5 | 4 |
+----------+------------+------------+-----------+---------+
Thank you!
You want a cumulative sum and to subtract it from the stock table. In SQL Server 2012+:
select s.*,
(st.stk_qty -
sum(s.sales_qty) over (partition by s.sales_item order by sales_date)
) as ava_stk
from sales s join
stock st
on s.sales_item = st.stk_name;

Row number by 3 distincts fields in SQL Server

I have this table:
| RecordLocator | DepartureStation | ArrivalStation | JourneyNumber | SegmentNumber | LegNumber | FlightNumber |
|---------------|------------------|----------------|---------------|---------------|-----------|--------------|
| DADABC | MAO | GRU | 2 | 1 | 1 | 1421 |
| CEDLDA | MAO | STM | 1 | 2 | 1 | 1643 |
| CEDLDA | GRU | MAO | 1 | 1 | 1 | 1640 |
| DADABC | GRU | FLN | 1 | 1 | 1 | 1848 |
| CEDLDA | BEL | SLZ | 1 | 2 | 3 | 1643 |
| DADABC | GIG | FOR | 1 | 2 | 3 | 1154 |
| CEDLDA | SLZ | FOR | 1 | 2 | 4 | 1680 |
| CEDLDA | FOR | REC | 1 | 2 | 5 | 1680 |
| DADABC | FOR | MAO | 1 | 2 | 4 | 1982 |
| CEDLDA | REC | SSA | 1 | 2 | 6 | 1680 |
| CEDLDA | STM | BEL | 1 | 2 | 2 | 1643 |
| DADABC | POA | GIG | 1 | 2 | 2 | 1201 |
| CEDLDA | SSA | GRU | 1 | 3 | 1 | 1817 |
| DADABC | FLN | POA | 1 | 2 | 1 | 1201 |
I want add a new column row number based on JourneyNumber, SegmentNumber and LegNumber, order by RecordLocator, for obtain this result:
| RecordLocator | DepartureStation | ArrivalStation | JourneyNumber | SegmentNumber | LegNumber | FlightNumber | rowNum |
|---------------|------------------|----------------|---------------|---------------|-----------|--------------|--------|
| CEDLDA | GRU | MAO | 1 | 1 | 1 | 1640 | 1 |
| CEDLDA | MAO | STM | 1 | 2 | 1 | 1643 | 2 |
| CEDLDA | STM | BEL | 1 | 2 | 2 | 1643 | 3 |
| CEDLDA | BEL | SLZ | 1 | 2 | 3 | 1643 | 4 |
| CEDLDA | SLZ | FOR | 1 | 2 | 4 | 1680 | 5 |
| CEDLDA | FOR | REC | 1 | 2 | 5 | 1680 | 6 |
| CEDLDA | REC | SSA | 1 | 2 | 6 | 1680 | 7 |
| CEDLDA | SSA | GRU | 1 | 3 | 1 | 1817 | 8 |
| DADABC | GRU | FLN | 1 | 1 | 1 | 1848 | 1 |
| DADABC | FLN | POA | 1 | 2 | 1 | 1201 | 2 |
| DADABC | POA | GIG | 1 | 2 | 2 | 1201 | 3 |
| DADABC | GIG | FOR | 1 | 2 | 3 | 1154 | 4 |
| DADABC | FOR | MAO | 1 | 2 | 4 | 1982 | 5 |
| DADABC | MAO | GRU | 2 | 1 | 1 | 1421 | 6 |
I tried this query:
SELECT
RecordLocator,
DepartureStation, ArrivalStation,
JourneyNumber, SegmentNumber,
LegNumber, FlightNumber,
(SELECT ((P.JourneyNumber + P.SegmentNumber + P.LegNumber))
FROM PAX P2
WHERE P2.RecordLocator = P.RecordLocator
AND P2.DepartureStation = P.DepartureStation
AND P2.ArrivalStation = P.ArrivalStation
AND P2.JourneyNumber = P.JourneyNumber
AND P2.SegmentNumber = P.SegmentNumber
AND P2.LegNumber = P.LegNumber
AND P2.FlightNumber = P.FlightNumber) AS rowNum
FROM
PAX P
I'm trying sum the columns JourneyNumber, SegmentNumber and LegNumber but does not work, i belive best way to do, is based on Recordlocator define a "weight" for the columns JourneyNumber > SegmentNumber > LegNumber, but i don't know how implement it.
In C# I know how to do it, using align for:
// First `for` - Journey
for(int i = 0; i < Journey.Count(); i++)
{
// Second `for` - Segment
for(int j = 0; j < Segment.Count(); j++)
{
// Third `for` - Leg
for(int k = 0; k < Leg.Count(); k++)
{
result = i + j + k;
}
}
}
Basically, breaking down your question into its parts, you want to re-start numbering for each RecordLocator, then you order it by the 3 fields you wanted in ascending order.
Overall, that's the idea of the ROW_NUMBER() which allows for ordering your records with a partition set for RecordLocator to re-start the numbering as needed. So, something like this should do it:
ROW_NUMBER() OVER(PARTITION BY RecordLocator ORDER BY JourneyNumber, SegmentNumber, LegNumber)
And your final SQL, therefore, becoming:
SELECT RecordLocator
, DepartureStation
, ArrivalStation
, JourneyNumber
, SegmentNumber
, LegNumber
, FlightNumber
, ROW_NUMBER() OVER(PARTITION BY RecordLocator ORDER BY JourneyNumber, SegmentNumber, LegNumber) AS rowNum
FROM PAX P
Hope this does the trick!

Table data into Pivot

An Accounting Table has the sample data shown below:
(There could be more AcctgHeads than those shown here)
+-------+-----------+--------+-----+
| Loan | AcctgHead | Amount | D_C |
+-------+-----------+--------+-----+
| 1 | Principal | 10000 | D |
| 1 | Principal | 500 | C |
| 1 | Cash | 10000 | C |
| 1 | Cash | 500 | D |
| 2 | Principal | 5000 | D |
| 2 | Cash | 5000 | C |
| 2 | Cash | 300 | D |
| 2 | Principal | 300 | C |
| 1 | IntDue | 50 | D |
| 1 | IntIncome | 50 | C |
+-------+-----------+--------+-----+
The desired ouput is:
+------+-------------+-------------+--------+--------+----------+----------+-------------+-------------+
| Loan | Principal_D | Principal_C | Cash_D | Cash_C | IntDue_D | IntDue_C | IntIncome_D | IntIncome_C |
+------+-------------+-------------+--------+--------+----------+----------+-------------+-------------+
| 1 | 10000 | 500 | 500 | 10000 | 50 | 0 | 0 | 50 |
| 2 | 5000 | 300 | 300 | 5000 | 0 | 0 | 0 | 0 |
+------+-------------+-------------+--------+--------+----------+----------+-------------+-------------+
What would be the query to accomplish this?
Thanks in advance for the help.
Try this: (assuming your acctghead has fixed number of values as shown)
select loan,
isnull(Principal_D,0) Principal_D,
isnull(Principal_C,0) Principal_C,
isnull(Cash_D,0) Cash_D,
isnull(Cash_C,0) Cash_C,
isnull(IntDue_D,0) IntDue_D,
isnull(IntDue_C,0) IntDue_C,
isnull(IntIncome_D,0) IntIncome_D,
isnull(IntIncome_C,0) IntIncome_C
from
(select loan, amount, AcctgHead + '_' + D_C As AcctgHeadDC from t) t
pivot
(
max(amount) for AcctgHeadDC in
(Principal_D,Principal_C,Cash_D,Cash_C,
IntDue_D,IntDue_C,IntIncome_D,IntIncome_C)
) p
SQL DEMO

Resources