Multiple times Grouping with where in SQL Server - sql

I have a source table like this
ProductName SaleReceipt SaleCode
--------------------------------
F-Apple 1001 1
F-Orange 1002 2
G-Rice 1003 3
G-Barile 1005 4
G-Oats 1006 1
V-Carrot 1007 4
V-Cabbage 1008 3
V-Potato 1009 1
V-Tomato 1010 1
Chocolate 1011 4
Cookies 1012 1
Cakes 1013 2
I need to create a report like this
30 Day delay 60 Day Delay 90 day delay 120 day delay
Fruits 1 1 0 0
Grains 1 0 1 1
Vegetables 2 0 1 1
Other category 1 1 0 1
The conditions to create the report are:
All ProductName start with F is grouped as fruits
All ProductName start with G is grouped as Grains
All ProductName start with V is grouped as Vegetables
Other product name go in the other category
30 day delay: count of (SaleReceipt) with a SaleCode=1
60 day delay: count of (SaleReceipt) with a SaleCode=2
90 day delay: count of (SaleReceipt) with a SaleCode=3
120 day delay: count of (SaleReceipt) with a SaleCode>=4
I could not find how to do grouping two times. I am using SQL Server 2014.

You can use case in a group by. Or use a lookup table:
select coalesce(c.name, 'Other category'),
sum(case when salescode = 1 then 1 else 0 end) as salecode_1,
sum(case when salescode = 2 then 1 else 0 end) as salecode_2,
sum(case when salescode = 3 then 1 else 0 end) as salecode_3,
sum(case when salescode = 4 then 1 else 0 end) as salecode_4
from t left join
(values ('F-%', 'Fruit'), ('G-%' , 'Grain'), ('V-%', 'Vegetable')
) c(pattern, name)
on t.productname like c.pattern
group by coalesce(c.name, 'Other category');

Related

Group by one column and return several columns on multiple conditions - T-SQL

I have two tables which I can generate with SELECT statements (joining multiple tables) as follows:
Table 1:
ID
Site
type
time
1
Dallas
2
01-01-2021
2
Denver
1
02-01-2021
3
Chicago
1
03-01-2021
4
Chicago
2
29-11-2020
5
Denver
1
28-02-2020
6
Toronto
2
11-05-2019
Table 2:
ID
Site
collected
deposited
1
Denver
NULL
29-01-2021
2
Denver
01-04-2021
29-01-2021
3
Chicago
NULL
19-01-2020
4
Dallas
NULL
29-01-2019
5
Winnipeg
13-02-2021
17-01-2021
6
Toronto
14-02-2020
29-01-2020
I would like the result to be grouped by Site, having on each column the COUNT of type=1 , type=2, deposited and collected, all of the 4 columns between a selected time interval. Example: (interval between 01-06-2020 and 01-06-2021:
Site
type1
type2
deposited
collected
Dallas
0
1
0
0
Denver
1
0
2
1
Chicago
1
1
0
0
Toronto
0
0
0
0
Winnipeg
0
0
1
1
How about union all and aggregation?
select site,
sum(case when type = 1 then 1 else 0 end) as type_1,
sum(case when type = 2 then 1 else 0 end) as type_2,
sum(deposited) as deposited, sum(collected) as collected
from ((select site, type, 0 as deposited, 0 as collected
from table1
) union all
(select site, null,
(case when deposited is not null then 1 else 0 end),
(case when collected is not null then 1 else 0 end)
from table2
)
) t12
group by site;
Combine your tables 1 and 2 with a join on Site
Use COUNT(CASE WHEN type = 1 then 1 END) as type1 and a similar construct for type 2
Use COUNT(CASE WHEN somedate BETWEEN '2020-06-01' and '2021-06-01' then 1 END) as ... for your dates

SQL merge 3 tables

I have an sql query involving 2 tables and try to add a third one.
These are the tables
FreeBookPos
FreeBooK_ID
ArticleNr
Amount
FreeBook
ID
BookNr
Date
FreeFields
FreeFieldType
Value
SQLPrimeKey
The first two are linked this way
select FreeBookPos.ArticleNr, Format(FreeBooking.Date, 'yyyy_MM') as dt,
SUM(CASE WHEN FreeBook.BookNr = 0 THEN FreeBookPos.Amount ELSE 0 END) as TotalEntryAmount,
SUM(CASE WHEN FreeBook.BookNr = 1 THEN FreeBookPos.Amount ELSE 0 END) as TotalLeftAmount
From FreeBookPos
INNER JOIN FreeBook on FreeBookPos.FreeBook_ID = FreeBook.ID
group by FORMAT ( FreeBook.Date, 'yyyy_MM'), FreeBookPos.ArticleNr
order by dt, ArticleNr
Now I need to add the table 3. This table is linked via SQLPrimeKey to FeeBook table ID. I then need to have only the fields where FreeFields.Value 2 or 4 and FreeFields.FreeFieldType = 54.
I tried various options with join but never get the result. Would I need to first join table 2 and 3 and then with 1 in a separate step?
Table 1: FreeBookPos
FreeBook_ID ArticleNr Amount
1 145 12
2 145 6
3 143 4
4 145 1
5 145 42
Table 2: FreeBook
ID BookNr Date
1 1 2012-05-19
2 -1 2012-05-21
3 1 2012-05-22
4 -1 2012-05-24
5 -1 2012-06-25
Table 3: FreeFields
SQLPrimareyKey FreeFieldType Value
1 54 1
2 52 2
3 54 4
4 54 2
5 54 2
Result should be:
ArticleNr Dt TotalEntryAmount TotalLeftAmount
143 2012-05 4 0
145 2012-05 0 -1
145 2012-06 0 -42
Try the below -
select FreeBookPos.ArticleNr, Format(FreeBooking.Date, 'yyyy_MM') as dt,
SUM(CASE WHEN FreeBook.BookNr = 0 THEN FreeBookPos.Amount ELSE 0 END) as TotalEntryAmount,
SUM(CASE WHEN FreeBook.BookNr = 1 THEN FreeBookPos.Amount ELSE 0 END) as TotalLeftAmount
From FreeBookPos
INNER JOIN FreeBook on FreeBookPos.FreeBook_ID = FreeBook.ID
inner join FreeFields on FreeBook.ID=SQLPrimareyKey
where value in (2,4) and FreeFieldType = 54
group by FORMAT ( FreeBook.Date, 'yyyy_MM'), FreeBookPos.ArticleNr
order by dt, ArticleNr

Need help writing query that inserts a new row drawing its data from two rows under the same ID

I am writing a data fix SQL script that is to insert a new record using data from two rows that are under the same ID.
My table looks like the following:
AccountID | ActivityId | Debit | Credit | DisplayDetails | TransactionDate
=========================================================================
1 1 100 0 Display Details1 2015-02-02
1 2 0 0 Display Details1 2018-02-02
1 1 300 0 Display Details1 1999-02-02
1 2 0 0 Display Details1 2000-02-02
2 1 200 0 Display Details2 2017-02-02
2 2 0 0 Display Details2 2017-04-06
2 12 0 200 Display Details2 2015-04-06
3 1 200 0 Display Details3 2015-02-04
3 3 0 200 Display Details3 2015-06-02
4 1 100 0 Display Details4 2016-02-02
4 2 0 0 Display Details4 2016-06-02
ActivityId 1 writes to Debit
ActivityId 2 writes to neither, but is needed in my conditions
ActivityId 3 writes to Credit
ActivityId 12 writes to Credit
My data fix is to find ActivityId 1 with an associated ActivityId 2 but NOT an associated 12 or 3 under the same AccountID.
I have written a query that meets these conditions:
SELECT A.AccountID
INTO #temp
FROM Account A WITH(NOLOCK)
WHERE ActivityId IN (1, 2, 3, 12)
GROUP BY A.AccountID
HAVING SUM(CASE WHEN A.ActivityId = 1 THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN A.ActivityId = 2 THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN A.ActivityId = 3 THEN 1 ELSE 0 END) = 0 AND
SUM(CASE WHEN A.ActivityId = 12 THEN 1 ELSE 0 END) = 0;
Now that I have the affected data, I need to insert a new record with ActivityId 12 using DisplayDetails from the associated Accounts ActivityId 2 and TransactionDate from ActivityId 1.
I also need to insert to field Credit using the Debit value from ActivityId 1.
The background behind this is each Activity 1 and 2 under the same AccountId SHOULD have an ActivityId 12 too, however some of these were missed, hence the need for this fix script.
Below is my approach:
-- Uses AccountIds retrieved above to then get data from parent table.
SELECT A.AccountID, A.ActivityId, A.DebitAmount, A.CreditAmount, A.DisplayDetails, A.TransactionDate
INTO #temp2
FROM Account A
JOIN #temp T
ON T.AccountID = A.AccountID
-- Get ActivityId 2 data
SELECT *
INTO #ActivityTwo
FROM #temp2 T
WHERE T.ActivityId = 2
INSERT INTO Account (AccountID, ActivityId, DebitAmount, CreditAmount, DisplayDetails, TransactionDate)
SELECT T.AccountID, 12, 0, T.DebitAmount, S.DisplayDetails, T.TransactionDate
FROM #temp2 T WITH(NOLOCK)
JOIN #ActivityTwo S
ON T.AccountID = S.AccountID
WHERE T.ActivityId = 1
However if I have multiple Activity 1 and Activity 2 under the same AccountId, my script inserts duplicate rows.
AccountID | ActivityId | Debit | Credit | DisplayDetails | TransactionDate
=========================================================================
1 1 100 0 Display Details1 2015-02-02
1 2 0 0 Display Details1 2018-02-02
1 1 300 0 Display Details1 1999-02-02
1 2 0 0 Display Details1 2000-02-02
1 12 0 100 Display Details1 2015-02-02
1 12 0 100 Display Details1 2015-02-02
1 12 0 300 Display Details1 1999-02-02
1 12 0 300 Display Details1 1999-02-02
2 1 200 0 Display Details2 2017-02-02
2 2 0 0 Display Details2 2017-04-06
2 12 0 200 Display Details2 2015-04-06
3 1 200 0 Display Details3 2015-02-04
3 3 0 200 Display Details3 2015-06-02
4 1 100 0 Display Details4 2016-02-02
4 2 0 0 Display Details4 2016-06-02
4 12 0 100 Display Details4 2016-02-02
As you can see, AccountID 1 has 4 extra 12 records, when it should only have two 12 records.
AccountID 4 worked correctly though, so I believe the problem lies with an account having multiple ActivityId 1 and 2s.
Why is this happening? What can I do to fix this?
Does anyone have a better approach to this problem?
Any help would be greatly appreciated.
Thanks!
Since you do not have any way to associate Activity 1 rows with Activity 2 rows, I think you need a SELECT DISTINCT on your second insert.
SELECT DISTINCT A.AccountID, A.ActivityId, A.DebitAmount, A.CreditAmount,
A.DisplayDetails, A.TransactionDate
INTO #temp2
FROM Account A
JOIN #temp T
ON T.AccountID = A.AccountID

Limit sql query to only rows where aggregate of col = 0

I'm looking at auction data in the following form:
product auction_id bid_value is_winning_bid reject_reason
iPhone 123 5 1 0
iPhone 123 3 0 1
iPhone 123 2 0 200
iPad 456 1 0 1
iPad 456 10 0 1
iPhone 789 2 0 200
iPhone 999 10 0 1
I want to count the number of auctions where one of the bids had a reject_reason = 200 and none of the bids has a is_winning_bid = 1, grouped by product.The data set is large so I don't want to have the output grouped by auction_id.
The table above should result in:
product total_auctions count_unfilled_auctions count_unfilled_auctions_200
iPhone 3 2 1
iPad 1 1 0
I think you just need two levels of aggregation, one at the auction level and one at the product level:
select product, count(*) as total_auctions,
sum(1 - has_winning_bid) as unfulfilled_auctions,
sum(case when cnt_reject_200 > 0 and has_winning_bid = 1 then 1 else 0 end) as unfulfilled_auctions_200
from (select auction_id, product,
max(is_winning_bid) as has_winning_bid,
sum(case when reject_reason = 200 then 1 else 0 end) as cnt_reject_200
from auctiondata ad
group by auction_id, product
) ad
group by product;

SQL Query. limit an update per rows if condition is X and Y for the same ID number

Have the following table tblTrans where
Trans_ID Trans Sequence Trans_PointsEarned Trans_PointsApplied
4452 1 1 1
4452 2 1 1
4452 3 0 1
4462 1 1 1
4462 2 1 1
4462 3 1 1
4462 4 1 1
4462 5 1 1
9101 1 0 1
9101 2 0 1
9101 3 0 1
9101 4 0 1
(useless table doesnt work)
I need to set the following on another field per every customer ID.
So Customer_OverallPoints
4452 = 2 (doesn't count 0's)
4462 = 4 (I want to cap the points to 4 based on the sequence and transID and customerID)
9101 = 0 (dont count 0's).
This needs to be applied to thousands of records based on customerID and TransID where Trans_Sequence is within the same Trans_ID and it only counts the first 4 rows that have the Trans_pointsEarned = 1.
I tried putting a psuedocode together but it just looked ridicilous and I can't even come up with the logic for this.
Thanks
Assuming that TransId is really the customer id, I think the basic logic is just an aggregation:
select t.TransId,
(case when sum(t.Trans_PointsEarned) > 4 then 4
else sum(t.Trans_PointsEarned)
end) as Customer_OverallPoints
from tblTrans t
group by t.TransId;
You can put this into an update statement as:
update customers c
set Customer_OverallPoints = (select (case when sum(t.Trans_PointsEarned) > 4 then 4
else sum(t.Trans_PointsEarned)
end)
from tblTrans t
where t.TransId = c.CustomerId
);