Select that returns only new status changes - sql

I would like to select all the accounts that status changed to B1 during the month of December 2020. That is, the accounts that now have B1 status and in the previous record did not have.
Records with VALID_TO_DATE = 31/12/2100 (DD/MM/YYYY) are the most recent records.
However, there are cases, such as account 400, that the change in status occurred in the penultimate record, which is no longer valid. But as the change happened in December and the status is the same as the valid record, my select statement must include account 400.
Desired result: In this sense, according to the data below, I need a select that returns only accont 100 and 400
ROW
ACCOUNT_ID
ACCOUNT_CHANGE_DATE
VALID_TO_DATE
ACCOUNT_STATUS
1
100
21/10/2020
05/12/2020
A1
2
100
05/12/2020
15/12/2020
A1
3
100
15/12/2020
31/12/2100
B1
4
200
09/11/2020
22/12/2020
A1
5
200
22/12/2020
25/12/2020
A1
6
200
25/12/2020
31/12/2100
A1
7
300
11/11/2020
19/11/2020
B1
8
300
19/11/2020
23/12/2020
A1
9
300
23/12/2020
31/12/2100
A1
10
400
16/11/2020
14/12/2020
A1
11
400
14/12/2020
18/12/2020
B1
12
400
18/12/2020
31/12/2100
B1
My source table has records of all changes that affected each of the accounts, which may or may not change the status of the respective account.
This is my incomplete/wrong attempt:
SELECT ACCOUNT_ID
FROM TABLE t1
where ACCOUNT_STATUS ="B1"
AND ACCOUNT_CHANGE_DATE BETWEEN '01/12/2020' AND '31/12/2020'
AND VALID_TO_DATE = '31/12/2100'
AND NOT EXISTS (SELECT * FROM TABLE t2
WHERE t2.ACCOUNT_ID = t1.ACCOUNT_ID
AND t2.ACCOUNT_STATUS = 'B1'
AND t2.ACCOUNT_CHANGE_DATE < t1.ACCOUNT_CHANGE_DATE);

I would like to select all the accounts that status changed to B1 during the month of December 2020.
Use lag():
select t1.*
from (select t1.*,
lag(status) over (partition by account_id order by ACCOUNT_CHANGE_DATE) as prev_status
from t1
) t1
where prev_status <> 'B1' and status = 'B1' and
account_change_date >= '2021-12-01' and
account_change_date < '2022-01-01'

Related

Get all numbers 1-100 per ID even without data

I have a table which looks like this:
ID
money_earned
days_since_start
1
1000
1
1
2000
2
1
3000
4
1
2000
5
2
1000
1
2
100
3
I want that rows, without a days_since_start (which means that the money_earned column was empty that day) - will include all the days PER ID, with "money" being null to indicate there was no earnings, so it to look like this:
ID
money_earned
days_since_start
1
1000
1
1
2000
2
1
NULL
3
1
3000
4
1
2000
5
2
1000
1
1
NULL
2
2
100
3
I have tried to look up for something like that, but I don't even know what function does that...
thank you!
You can first generate table for your ids and days by query
SELECT d1.id, generate_series(1, max(d1.days_since_start)) AS days_since_start
FROM days d1 JOIN days d2 ON d1.id = d2.id GROUP BY d1.id
(if you need all numbers from 1 to 100, you can replase exspression max(d1.days_since_start) with the number 100)
and then join it with your table. the final query might look like this
WITH genDays AS
(SELECT d1.id, generate_series(1, max(d1.days_since_start)) AS days_since_start
FROM days d1 JOIN days d2 ON d1.id = d2.id GROUP BY d1.id)
SELECT coalesce(genDays.id, d3.id) AS id,
d3.money_earned,
coalesce(d3.days_since_start, genDays.days_since_start) AS days_since_start
FROM days d3 FULL JOIN genDays ON genDays.id = d3.id
AND genDays.days_since_start = d3.days_since_start
the output will be as you need
if you need to fill the nulls with last nonnull value per id then you can modify the query like here
WITH genDays AS
(SELECT d1.id as id, generate_series(1, 100) AS days_since_start
FROM days d1 JOIN days d2 ON d1.id = d2.id GROUP BY d1.id)
SELECT coalesce(genDays.id, d3.id) AS id,
coalesce(d3.money_earned,
(
select d4.money_earned
from days d4
where d4.id = genDays.id
and d4.days_since_start < genDays.days_since_start
order by d4.days_since_start desc
limit 1
)) as money_earned,
coalesce(d3.days_since_start, genDays.days_since_start) AS days_since_start
FROM days d3 FULL JOIN genDays ON genDays.id = d3.id
AND genDays.days_since_start = d3.days_since_start

Pull record between 2 columns

I have 2 tables
table1
ID VendorID
100 11190
200 99999
table2
ID VendorID
100 11190
100 11190
200 12523
200 53266
My expect result and my goal if ID and VendorID from table1 match with ID and VendorID from table2 then flag NO
table1ID table1Vendor table2ID table2Vendor Code
100 12345 100 12345 No
100 12345 100 12345 No
100 12345 100 45678 No
200 56489 200 11111 Use
200 56489 200 22222 Use
My query
SELECT a.id as table1ID, a.vendorid as table1Vendor, b.id as table2ID, b.Vendorid as table2Vendor
, case
when a.vendorid <> b.Vendorid
then 'Use' else 'No'
end as Code
FROM table_1 a
JOIN table_2 b on a.id = b.id
But I got
table1ID table1Vendor table2ID table2Vendor Code
100 12345 100 12345 No
100 12345 100 12345 No
100 12345 100 45678 Use
200 56489 200 11111 Use
200 56489 200 22222 Use
You can see row 3 is incorrect, should code as NO cause 12345(table1vendor) still match with 12345(table2vendor)
Not sure why, need some help. Thank you.
You are only checking the same row for a match, but according to your logic you need to check all rows, therefore you need another sub-query.
SELECT a.ID AS table1ID, a.VendorID AS table1Vendor, b.ID AS table2ID, b.VendorID as table2Vendor
, CASE WHEN EXISTS (SELECT 1 FROM table_2 b1 WHERE b1.id = a.ID AND b1.VendorID = a.VendorID) THEN 'No' ELSE 'Use' END AS Code
FROM table_1 a
JOIN table_2 b ON a.ID = b.ID;

Deleting rows in sql rows based on pair value

I have SQL test table as below:
Item
Doc_No
Code
Line_Item
1
abc1234
101
01
2
abc1234
102
01
3
def5678
101
01
4
def5678
102
01
5
ghi1234
101
01
6
ghi1234
101
02
7
jkl5678
101
01
I am trying to eliminate rows when duplicate values of "Doc_No" has pair values of "101" and "102" in "Code" column e.g abc1234 and def5678.
At the same time I want to maintain duplicate values of "Doc_No" without the pair value of "101" and "102" in "Code" column e.g. ghi1234. Final output as below:
Item
Doc_No
Code
Line_item
5
ghi1234
101
01
6
ghi1234
101
02
7
jkl5678
101
01
I tried to get the rows with duplicate values and exclude them but this will wrongly exclude "ghi1234" and not the final output table I want.
SELECT
a.*,
b.Count_Item
FROM dbo.test AS a
LEFT JOIN
(SELECT Doc_No, COUNT(*) AS Count_Item
FROM dbo.test
GROUP BY Doc_No
HAVING COUNT(*) > 1) AS b
ON a.Doc_No = b.Doc_No
WHERE b.Count_Item < 2
Item
Doc_No
Code
Line_Item
Count_Item
7
jkl5678
101
01
1
Can somebody help me with this?
Building on your construct:
SELECT a.*
FROM dbo.test a LEFT JOIN
(SELECT Doc_No, COUNT(*) AS Count_Item
FROM dbo.test
WHERE code IN (101, 102)
GROUP BY Doc_No
) b
ON a.Doc_No = b.Doc_No
WHERE b.Count_Item < 2 OR Count_Item IS NULL;
Note: This assumes that codes are not duplicated for a doc.
What about this:
select *
from test
group by Doc_No, Line_Item
having count(*) = 1
or this:
select *
from test
where Doc_No not in (
select Doc_No
from test
join test as test2 using (Doc_No)
where test.Code = 101 and test2.Code = 102
);

Sql - Getting Sum on the join table

I just want to get the result which displays the reference which is not tallied in sum of table2. when i run my query below it will give me an wrong sum which it gets doubled even if group by cusid ,refno.
Table 1
RefNo
CusID
TotalAmount
1
1001
50
2
1001
30
3
1002
40
Table 2
RefNo
CusID
Particular
Amount
1
1001
Paper
30
1
1001
Pencil
30
2
1001
Ball
15
2
1001
Rubber
20
3
1002
Laptop
50
select * from Table1 a
INNER JOIN (Select CusID,RefNo, SUM(Amount) as CorrectTotal from Table2 group by
CusID,RefNo,
)b
ON b.CusID= a.CusID AND b.RefNo= a.RefNo
where a.TotalAmount != CorrectTotal
Expected Result
If you do it with FULL JOIN and with GROUP BY, you will also get rows where there is no record in the other table.
SELECT COALESCE(a.RefNo, b.RefNo) AS RefNo
, COALESCE(a.CusID, b.CusID) AS CusID
, a.TotalAmount
, SUM(b.Amount) AS CorrectTotal
FROM table1 a
FULL JOIN table2 b ON a.RefNo = b.RefNo
AND a.CusID = b.CusID
GROUP BY COALESCE(a.RefNo, b.RefNo)
, COALESCE(a.CusID, b.CusID)
, a.TotalAmount
ORDER BY 1, 2
Output
RefNo
CusID
TotalAmount
CorrectTotal
1
1001
50
60
2
1001
30
35
3
1002
40
50
8
888
88
(null)
9
999
(null)
99
See running demo on SQL Fiddle.
The other answer will work, but if you don't want to mess with a GROUP BY on the whole query you can also use an APPLY to do this:
SELECT a.*, c.CorrectAmount
FROM Table1 a
OUTER APPLY (
SELECT SUM(Amount) AS CorrectAmount
FROM Table2 b
WHERE b.CusID = a.CusID AND b.RefNo = a.RefNo
) c
WHERE a.TotalAmount <> c.CorrectAmount

sql server group by different columns

I have my data looking like this
Amount Officer Branch
100 S1 B1
200 S1 B2
300 S1 B3
100 S2 B1
200 S2 B2
300 S2 B3
I need another column which can show the totals by officer
Amount Officer Branch TotalByOfficer
100 S1 B1 500
200 S1 B2 500
300 S1 B3 500
100 S2 B1 900
200 S2 B2 900
600 S2 B3 900
Once i have this, I can use a having clause to filter by TotalByOfficer.
How do I accomplish such a thing.
You just need to do a SUM() OVER a PARTITION on Officer:
Select Amount,
Officer,
Branch,
Sum(Amount) Over (Partition By Officer) As TotalByOfficer
From YourTable
SELECT amount, officer, branch,
SUM(amount) OVER (PARTITION BY officer) as TotalByOfficer
FROM Table
Note, you can only use "HAVING" if you are using group by which I'm not. Use this as a sub query and add a filter, like this
SELECT *
FROM (
SELECT amount, officer, branch,
SUM(amount) OVER (PARTITION BY officer) as TotalByOfficer
FROM Table
) X
WHERE TotalByOfficer <> 500