I have two tables
accounts table
account_id location_id
1 11
1 12
2 21
2 22
Events_table
location_id events_id event_date
11 e1 2022/03/04
11 e3 2022/03/05
12 e2 2022/03/10
21 e5 2022/04/10
21 e2 2022/04/09
The result I expected is to get only latest event_id for location with respect to account
Result Expected:
account_id location_id events_id event_date
1 11 e3 2022/03/05
1 12 e2 2022/03/10
2 21 e5 2022/04/10
Use:
with cte as
( select *,
row_number() over(partition by location_id order by event_date desc ) row_num
from Events
) select a.account_id,
a.location_id,
cte.events_id,
cte.event_date
from accounts a
inner join cte on cte.location_id=a.location_id
where cte.row_num=1;
Demo
Related
I would like to allocate rows from one table to another using a sort on TopLvlOrd field. The inputs are the [Orders] table and the [Defects] table. I would like to create an SQL that produces [Output]. Even after a bunch of online research I'm not sure how to do this. I'd prefer not to do a cursor, but will go there if necessary. Any ideas? Using SQL Server 2012.
Rules:
(1) Allocate by TopLvlOrd asc,
(2) Allocate one TopLvlOrd row per PegQty
[Orders]
TopLvlOrd IntOrd PegQty
========= ====== ======
67 25 3
120 25 1
111 25 1
16 25 1
127 25 1
127 65 1
127 85 1
[Defects]
DefectID IntOrd TotQty
======== ====== ======
1 25 10
2 25 10
3 25 10
4 25 10
5 25 10
6 25 10
7 25 10
8 25 10
9 25 10
10 25 10
11 65 1
12 85 2
13 85 2
[Output]
DefectID IntOrd TotQty TopLvlOrd
======== ====== ====== =========
1 25 10 16
2 25 10 67
3 25 10 67
4 25 10 67
5 25 10 111
6 25 10 120
7 25 10 127
8 25 10 NULL
9 25 10 NULL
10 25 10 NULL
11 65 1 127
12 85 2 127
13 85 2 NULL
This answers the original version of the question.
I think you want to join on an implicit sequence number, which you can add using row_number():
select d.*, o.*
from (select d.*,
row_number() over (partition by intord order by defectid) as seqnum
from defects d
) d left join
(select o.*,
row_number() over (partition by IntOrd order by TopLvlOrd) as seqnum
from orders o
) o
on d.intord = o.intord and d.seqnum = o.seqnum
Please another time make another question. Please check this query:
SELECT DefectID, IntOrd,
TotQty, TopLvlOrd, PegQty
FROM
(
SELECT B. DefectID, COALESCE (A.IntOrd, B. IntOrd) IntOrd,
B.TotQty, A. TopLvlOrd, A.PegQty FROM
(
SELECT TopLvlOrd ,IntOrd, ROW_NUMBER () OVER (PARTITION By IntOrd ORDER by
TopLvlOrd) Num, PegQty FROM Orders
) A
FULL JOIN
(
SELECT DefectID , IntOrd ,TotQty, ROW_NUMBER () OVER (PARTITION By IntOrd ORDER by
TotQty) Num FROM Orders
) B
ON A. IntOrd=B.IntOrd AND A.Num=B.Num
)C
JOIN
master.dbo.spt_values Tab
ON Tab.type='P' AND Tab.number<C.PegQty
SELECT B. DefectID, COALESCE (A.IntOrd, B. IntOrd) IntOrd,
B.TotQty, A. TopLvlOrd FROM
(
SELECT TopLvlOrd ,IntOrd , ROW_NUMBER () OVER (PARTITION By IntOrd ORDER by TopLvlOrd) Num FROM Orders
) A
FULL JOIN
(
SELECT DefectID , IntOrd ,TotQty, ROW_NUMBER () OVER (PARTITION By IntOrd ORDER by TotQty) Num FROM Orders
) B
ON A. IntOrd=B.IntOrd AND A. Num=B.Num
The following is my syntax
with AA as
(
select distinct Store_Code, sum([Transactions]) as totaltrans
from CRM_RETAIL_SUMMARY
where Store_Code in ('L43','J62','L45','UA2','A35','949','C50','L44','Y45')
and Trans_Hdr_Sale_Date BETWEEN '2018-12-1' AND '2018-12-31'
group by Transactions, Store_Code
),
BB as
(
select distinct[Store_Code] as storecode from CRM_RETAIL_SUMMARY
)
select AA.totaltrans, BB.storecode
from AA
inner join BB on AA.Store_Code = BB.storecode
and then the result shows this:
storecode totaltrans
----------------------
A35 65
A35 76
A35 48
A35 62
A35 56
UA2 5
UA2 6
UA2 8
UA2 15
UA2 9
UA2 16
949 16
949 29
949 55
But I want to SUM each of the storecode like the following but I don't know how:
storecode totaltrans
---------------------
A35 307
UA2 59
Thank you for all answers
You only need to group by store_code, not store_code, transactions. and you don't need the 2nd cte.
with AA as (
select Store_Code, sum([Transactions]) as totaltrans
from CRM_RETAIL_SUMMARY
where Store_Code in ('L43','J62','L45','UA2','A35','949','C50','L44','Y45')
and Trans_Hdr_Sale_Date BETWEEN '2018-12-1' AND '2018-12-31'
group by Store_Code
)
select * from AA;
Would you just use group by?
select BB.storecode, sum(AA.totaltrans)
from AA join
BB
on AA.Store_Code = BB.storecode
group by BB.storecode;
Of course, this goes after the CTEs.
I have a table:
table1
id e_id e_nm e_value line_num
59 BHT03-01 Ref ID 04/18/1820 4
59 BHT03-02 38 4
59 BHT03-03 10 4
59 ABC03-01 Ref ID 04/18/1820 4
59 ABC03-02 38 4
59 ABC03-03 10 4
60 BHT03-01 Ref ID 05/09/1820 4
60 BHT03-02 52 4
60 BHT03-03 43 4
I need to concatenate each BHT03-01, BHT03-02 and BHT03-03 separated by : into 1 BHT03-01 for each id and line_num.
All other e_id apart from BHT03-01 should be unaffected.
Here is the output:
table1
id e_id e_nm e_value line_num
59 BHT03-01 Ref ID 04/18/1820:38:10 4
59 BHT03-02 38 4
59 BHT03-03 10 4
59 ABC03-01 Ref ID 04/18/1820 4
59 ABC03-02 38 4
59 ABC03-03 10 4
60 BHT03-01 Ref ID 05/09/1820:52:43 4
60 BHT03-02 52 4
60 BHT03-03 43 4
Once I get this table, I also have to drop all the rows with BHT03-02, BHT03-03.
How can I do it in Oracle SQL?
Given that the updated value in the e_value column is actually derived data, I suggest just creating a computed column when you select, maybe in a view:
SELECT
id,
e_id,
e_nm,
CASE WHEN e_id LIKE 'BHT03%' AND
ROW_NUMBER() OVER (PARTITION BY id, line_num ORDER BY e_id) = 1
THEN LISTAGG(e_value, ':') WITHIN GROUP (ORDER BY e_id)
OVER (PARTITION BY id, line_num)
ELSE e_value END AS e_value,
line_num
FROM yourTable
ORDER BY
id,
e_id;
The above logic detects the first row in each id group, which should correspond to the row with the date in the e_value column. In the case of the first row, it displays a colon-separated concatenation of all the records in the id group, otherwise it just repeats the e_value which is already there.
The following statement will do the update and delete in one go:
MERGE INTO table1 tgt
USING (SELECT ID,
e_id,
line_num,
listagg(e_value, ':') WITHIN GROUP (ORDER BY e_id) OVER (PARTITION BY id, line_num) e_value,
CASE WHEN e_id IN ('BHT03-02', 'BHT03-03') THEN 'Y' ELSE 'N' END del
FROM table1
WHERE e_id IN ('BHT03-01', 'BHT03-02', 'BHT03-03')) src
ON (tgt.id = src.id AND tgt.line_num = src.line_num AND tgt.e_id = src.e_id) -- or whatever the unique identifiers are for the table1 rows
WHEN MATCHED THEN
UPDATE SET tgt.e_value = src.e_value
DELETE WHERE src.del = 'Y';
And here's a demo of it working.
I am attempting to remove transactions that have been reversed from a table. the table has Account, Date, Amount and Row. If a transaction has been reversed Account will match and Amount will be inverse of each other.
Example Table
Account Date Amount Row
12 1/1/18 45 72 -- Case 1
12 1/2/18 50 73
12 1/2/18 -50 74
12 1/3/18 52 75
15 1/1/18 51 76 -- Case 2
15 1/2/18 51 77
15 1/2/18 -51 78
15 1/2/18 51 79
18 1/2/18 50 80 -- Case 3
18 1/2/18 50 81
18 1/2/18 -50 82
18 1/2/18 -50 83
18 1/3/18 50 84
18 1/3/18 50 85
20 1/1/18 57 88 -- Case 4
20 1/2/18 57 89
20 1/4/18 -57 90
20 1/5/18 57 91
Desired Results Table
Account Date Amount Row
12 1/1/18 45 72 -- Case 1
12 1/3/18 52 75
15 1/1/18 51 76 -- Case 2
15 1/2/18 51 79
18 1/3/18 50 84 -- Case 3
18 1/3/18 50 85
20 1/1/18 57 88 -- Case 4
20 1/5/18 57 91
Removing all instances of inverse transactions does not work when there are multiple transactions when all other columns are the same. My attempt was to count all duplicate transactions, count of all inverse duplicate transactions, subtracting those to get the number of rows I needed from each transactions group. I was going to pull the first X rows but found in most cases I want the last X rows of each group, or even a mix (the first and last in Case 2).
I either need a method of removing pairs from the original table, or working from what I have so far, a method of distinguishing which transactions to pull.
Code so far:
--adding row Numbers
with a as (
select
account a,
date d,
amount f,
row_number() over(order by account, date) r
from table),
--counting Duplicates
b as (
select a.a, a.f, Dups
from a join (
select a, f, count(*) Dups
from a
group by a.a, a.f
having count(*)>1
) b
on a.a=b.a and
b.f=a.f
where a.f>0
),
--counting inverse duplicates
c as (
select a.a, a.f, InvDups
from a join (
select a, f, count(*) InvDups
from a
group by a.a, a.f
having count(*)>1
) b
on a.a=b.a and
-b.f=a.f
where a.f>0
),
--combining c and d to get desired number of rows of each transaction group
d as (
select
b.a, b.f, dups, InvDups, Dups-InvDups TotalDups
from b join c
on b.a=c.a and
b.f=c.f
),
--getting the number of rows from the beginning of each transaction group
select d.a, d.d, d.f
from
(select
a, d, f, row_number() over (group by a, d, f) r2
from a) e
join d
on e.a=d.a and
TotalDups<=r2
You can try this.
SELECT T_P.* FROM
( SELECT *, ROW_NUMBER() OVER(PARTITION BY Account, Amount ORDER BY [Row] ) RN from #MyTable WHere Amount > 0 ) T_P
LEFT JOIN
( SELECT *, ROW_NUMBER() OVER(PARTITION BY Account, Amount ORDER BY [Row] ) RN from #MyTable WHere Amount < 0 ) T_N
ON T_P.Account = T_N.Account
AND T_P.Amount = ABS(T_N.Amount)
AND T_P.RN = T_N.RN
WHERE
T_N.Account IS NULL
The following handles your three cases:
with t as (
select t.*,
row_number() over (partition by account, date, amount order by row) as seqnum
from table t
)
select t.*
from t
where not exists (select 1
from t t2
where t2.account = t.account and t2.date = t.date and
t2.amount = -t.amount and t2.seqnum = t.seqnum
);
Use This
;WITH CTE
AS
(
SELECT
[Row]
FROM YourTable YT
WHERE Amount > 0
AND EXISTS
(
SELECT 1 FROM YourTable WHERE Account = YT.Account
AND [Date] = YT.[Date]
AND (Amount+YT.Amount)=0
)
UNION ALL
SELECT
[Row]
FROM YourTable YT
WHERE Amount < 0
AND EXISTS
(
SELECT 1 FROM YourTable WHERE Account = YT.Account
AND [Date] = YT.[Date]
AND (Amount+YT.Amount)>0
)
)
SELECT * FROM YourTable
WHERE EXISTS
(
SELECT 1 FROM CTE WHERE [Row] = YourTable.[Row]
)
I have a compound primary key where the single parts are potentially random. They aren't in any particular order and one can be unique or they can be all the same.
I do not care which row I get. This is like "Just pick one from each group".
My table:
KeyPart1 KeyPart2 KeyPart3 colA colB colD
11 21 39 d1
11 22 39 d2
12 21 39 d2
12 22 39 d3
13 21 38 d3
13 22 38 d5
Now what I want is to get for each entry in colD one row, I do not care which one.
KeyPart1 KeyPart2 KeyPart3 colA colB colD
11 21 39 d1
12 21 39 d2
12 22 39 d3
13 22 38 d5
For rows that are unique by colD, you will have to decide which other column values will be discarded. Here, within the over clause I have use partition by colD which provides the wanted uniqueness by that column, but the order by is arbitrary and you may want to change it to suit your needs.
select
d.*
from (
select
t.*
, row_number() over (partition by t.colD
order by t.KeyPart1,t.KeyPart2,t.KeyPart) as rn
from yourtable t
) d
where d.rn = 1;
The following should work in almost any version of DB2:
select t.*
from (select t.*,
row_number() over (partition by KeyPart1, KeyPart2
order by KeyPart1
) as seqnum
from t
) t
where seqnum = 1;
If you only care about column d, and the first two key parts, then you can use group by:
select KeyPart1, KeyPart2, min(colD)
from t
group by KeyPart1, KeyPart2;
Change 'order by' if necessary
with D as (
select distinct ColdD from yourtable
)
select Y.* from D
inner join lateral
(
select * from yourtable X
where X.ColdD =D.ColdD
order by X.KeyPart1, X.KeyPart2, X.KeyPart3
fetch first rows only
) Y on 1=1