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

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

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

Multiple times Grouping with where in SQL Server

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');

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
);

Check value in rows

I have the table below
ID | PARCEL | STATUS | ORDER_ID
1 1 PENDING 1234
2 2 COMPLETE 1234
3 1 COMPLETE 9999
4 2 PENDING 9999
5 3 PENDING 9999
6 1 COMPLETE 1111
7 2 COMPLETE 1111
8 3 COMPLETE 1111
9 1 COMPLETE 3333
10 2 PENDING 3333
i need get results when the first parcel is PENDING and have more than one parcel.
i try with the sql:
SELECT * FROM table WHERE parcela = 1 AND status = 'pending'
group by order_id
having count(order_id) > 1
the answer for the query is:
ID | PARCEL | STATUS | ORDER_ID
1 1 PENDING 1234
2 2 COMPLETE 1234
You can get the orders using a having clause:
select order_id
from table
group by order_id
having count(order_id) > 1 AND
sum(case when parcela = 1 AND status = 'pending' then 1 else 0 end) > 0;
If you want the details, join the rows from the table back in on the order_id.
The problem with your query is that you are using a filter in the where clause that limits the rows to only the first row. This prevents the query from counting the total number of rows in each order.

Need Monthly Report in sql server query

I have 2 tables(Product1, product2).
Product1 Product2
------------------------------ -------------------------------------
PID ProdDesc ProdCode Date PID
------------------------------ -------------------------------------
1 Sony s001 2013/01/21 1
2 Samsung sa01 2013/02/27 1
3 LG L001 2013/03/14 2
4 Toshiba T001 2013/04/18 3
5 Philips P001 2013/05/30 4
2013/06/12 5
I need to generate a query that joins the data from the 2 tables and groups the results by month.
Here is my desired Output:
ProdDesc Jan2013 Feb2013 Mar2013 Apr2013 May2013 Jun2013 Jul2013 Aug2013 Sep2013 Oct2013 Nov2013 Dec2013
--------------------------------------------------------------------------------------------------
Sony 1 1 0 0 0 0 0 0 0
Samsung 0 0 1 0 0 0 0 0 0
LG 0 0 0 1 0 0 0 0 0
Toshiba 0 0 0 0 1 0 0 0 0
Philips 0 0 0 0 0 1 0 0 0
You'll need to use a PIVOT to project your result in columns, although some manipulation is required to prepare the data beforehand, to generate your column names and group data per month:
SELECT ProdDesc, [Jan2013],[Feb2013],[Mar2013],[Apr2013],
[May2013],[Jun2013]
FROM
(
SELECT ProdDesc, YearMonth, COUNT(P2PID) AS ProdCount
FROM
(
SELECT p1.ProdDesc,
CAST(DATENAME(MONTH, [DATE]) AS VARCHAR(3)) +
DATENAME(YEAR, [DATE]) AS YearMonth,
p2.PID as P2PID
FROM Product1 p1
LEFT JOIN Product2 p2
ON p1.PID = p2.PID
) x
GROUP BY ProdDesc, YearMonth
) y
PIVOT
(
SUM(ProdCount)
for [YearMonth] IN ([Jan2013],[Feb2013],[Mar2013],
[Apr2013],[May2013],[Jun2013])
) pv;
SqlFiddle here
It is quite likely that you'll want to determine the columns (MonYYYY) dynamically. Have a look here on how to do this.
You can use ISNULL or COALESCE to replace NULLS in the output, if necessary ISNULL([Jan2013], 0) AS Jan2013