How to get max sequence number? - sql

This is a continuation of my previous post : here
I have this query:
SELECT INVOICE_NUMBER, INVOICE_SEQ_NUMBER, FILE_NUMBER
FROM (SELECT A.INVOICE_NUMBER, A.INVOICE_SEQ_NUMBER, B.FILE_NUMBER,
DENSE_RANK() OVER (ORDER BY A.INVOICE_NUMBER) as seqnum
FROM TABLE1 A JOIN
TABLE2 B
ON A.INVOICE_NUMBER = B.INVOICE_NUMBER AND
A.INVOICE_SEQ_NUMBER = B.INVOICE_SEQ_NUMBER
) t
WHERE seqnum <= 3;
And this result:
-----------------------------------------------------
| INVOICE_NUMBER | INVOICE_SEQ_NUMBER | FILE_NUMBER |
------------------------------------------------------
|1111111111-1 | 1 | P4324324525 |
-----------------------------------------------------
|1111111111-1 | 2 | P4565674574 |
-----------------------------------------------------
|1111111111-1 | 3 | V4324552557 |
-----------------------------------------------------
|1111111111-1 | 4 | V4324552525 |
-----------------------------------------------------
|2222222222-2 | 1 | S4563636574 |
-----------------------------------------------------
|3333333333-3 | 1 | Q4324325675 |
-----------------------------------------------------
|3333333333-3 | 2 | Q4565674574 |
-----------------------------------------------------
So the new requirement is how do I get the maximum invoice sequence number for the same invoice number? The result should be like this:
------------------------------------------------------------------------
| INVOICE_NUMBER | INVOICE_SEQ_NUMBER | FILE_NUMBER |MAX_INV_SEQ_NUMBER|
------------------------------------------------------------------------
|1111111111-1 | 1 | P4324324525 | 4 |
------------------------------------------------------------------------
|1111111111-1 | 2 | P4565674574 | 4 |
------------------------------------------------------------------------
|1111111111-1 | 3 | V4324552557 | 4 |
------------------------------------------------------------------------
|1111111111-1 | 4 | V4324552525 | 4 |
------------------------------------------------------------------------
|2222222222-2 | 1 | S4563636574 | 1 |
------------------------------------------------------------------------
|3333333333-3 | 1 | Q4324325675 | 2 |
------------------------------------------------------------------------
|3333333333-3 | 2 | Q4565674574 | 2 |
------------------------------------------------------------------------

SELECT INVOICE_NUMBER, INVOICE_SEQ_NUMBER, FILE_NUMBER, MAX(INVOICE_SEQ_NUMBER) OVER (PARTITION BY INVOICE_NUMBER)
FROM (SELECT A.INVOICE_NUMBER, A.INVOICE_SEQ_NUMBER, B.FILE_NUMBER,
DENSE_RANK() OVER (ORDER BY A.INVOICE_NUMBER) as seqnum
FROM TABLE1 A JOIN
TABLE2 B
ON A.INVOICE_NUMBER = B.INVOICE_NUMBER AND
A.INVOICE_SEQ_NUMBER = B.INVOICE_SEQ_NUMBER
) t
WHERE seqnum <= 3;
Essentially, you just need this in your select statement:
MAX(INVOICE_SEQ_NUMBER) OVER (PARTITION BY INVOICE_NUMBER)

Add the following expression to the select list:
, max(INVOICE_SEQ_NUMBER) over (partition by INVOICE_NUMBER) as MAX_INV_SEQ_NUMBER.

Add an extra column in the selection part as below-
SELECT
INVOICE_NUMBER,
INVOICE_SEQ_NUMBER,
FILE_NUMBER,
(
SELECT COUNT(*)
FROM TABLE1 A
JOIN TABLE2 B
ON A.INVOICE_NUMBER = B.INVOICE_NUMBER
AND A.INVOICE_SEQ_NUMBER = B.INVOICE_SEQ_NUMBER
AND A.INVOICE_NUMBER = t.INVOICE_NUMBER
)MAX_INV_SEQ_NUMBER
FROM ........

Related

Each rows to column values

I'm trying to create a view that shows first table's columns plus second table's first 3 records sorted by date in 1 row.
I tried to select specific rows using offset from sub table and join to main table, but when joining query result is ordered by date, without
WHERE tblMain_id = ..
clause in joining SQL it returns wrong record.
Here is sqlfiddle example: sqlfiddle demo
tblMain
| id | fname | lname | salary |
+----+-------+-------+--------+
| 1 | John | Doe | 1000 |
| 2 | Bob | Ross | 5000 |
| 3 | Carl | Sagan | 2000 |
| 4 | Daryl | Dixon | 3000 |
tblSub
| id | email | emaildate | tblmain_id |
+----+-----------------+------------+------------+
| 1 | John#Doe1.com | 2019-01-01 | 1 |
| 2 | John#Doe2.com | 2019-01-02 | 1 |
| 3 | John#Doe3.com | 2019-01-03 | 1 |
| 4 | Bob#Ross1.com | 2019-02-01 | 2 |
| 5 | Bob#Ross2.com | 2018-12-01 | 2 |
| 6 | Carl#Sagan.com | 2019-10-01 | 3 |
| 7 | Daryl#Dixon.com | 2019-11-01 | 4 |
View I am trying to achieve:
| id | fname | lname | salary | email_1 | emaildate_1 | email_2 | emaildate_2 | email_3 | emaildate_3 |
+----+-------+-------+--------+---------------+-------------+---------------+-------------+---------------+-------------+
| 1 | John | Doe | 1000 | John#Doe1.com | 2019-01-01 | John#Doe2.com | 2019-01-02 | John#Doe3.com | 2019-01-03 |
View I have created
| id | fname | lname | salary | email_1 | emaildate_1 | email_2 | emaildate_2 | email_3 | emaildate_3 |
+----+-------+-------+--------+---------+-------------+---------------+-------------+---------------+-------------+
| 1 | John | Doe | 1000 | (null) | (null) | John#Doe1.com | 2019-01-01 | John#Doe2.com | 2019-01-02 |
You can use conditional aggregation:
select m.id, m.fname, m.lname, m.salary,
max(s.email) filter (where seqnum = 1) as email_1,
max(s.emailDate) filter (where seqnum = 1) as emailDate_1,
max(s.email) filter (where seqnum = 2) as email_2,
max(s.emailDate) filter (where seqnum = 3) as emailDate_2,
max(s.email) filter (where seqnum = 3) as email_3,
max(s.emailDate) filter (where seqnum = 3) as emailDate_3
from tblMain m left join
(select s.*,
row_number() over (partition by tblMain_id order by emailDate desc) as seqnum
from tblsub s
) s
on s.tblMain_id = m.id
where m.id = 1
group by m.id, m.fname, m.lname, m.salary;
Here is a SQL Fiddle.
Here is a solution that should get you what you expect.
This works by first ranking records within each table and joining them together. Then, the outer query uses aggregation to generate the expected output.
This solution will work even if the first record in the main table does not have id 1. Also filtering takes occurs within the JOINs, so this should be quite efficient.
SELECT
m.id,
m.fname,
m.lname,
m.salary,
MAX(CASE WHEN s.rn = 1 THEN s.email END) email_1,
MAX(CASE WHEN s.rn = 1 THEN s.emaildate END) email_date1,
MAX(CASE WHEN s.rn = 2 THEN s.email END) email_2,
MAX(CASE WHEN s.rn = 2 THEN s.emaildate END) email_date2,
MAX(CASE WHEN s.rn = 3 THEN s.email END) email_3,
MAX(CASE WHEN s.rn = 3 THEN s.emaildate END) email_date3
FROM
(
SELECT m.*, ROW_NUMBER() OVER(ORDER BY id) rn
FROM tblMain
) m
INNER JOIN (
SELECT
email,
emaildate,
ROW_NUMBER() OVER(PARTITION BY id ORDER BY emaildate) rn
FROM tblSub
) s
ON m.id = s.tblmain_id
AND m.rn = 1
AND s.rn <= 3
GROUP BY
m.id,
m.fname,
m.lname,
m.salary

sum of columns and list difference between rows

I am trying to get the difference between rows based on group by SELL_ID on the below table,
table1 - (table formatting courtesy of GitHub)
+---------+---------+----------+----------+------------------+---------+
| seq_ID | REQ_ID | CALL_ID | SELL_ID | REGION | COUNT |
+---------+---------+----------+----------+------------------+---------+
| 1 | 123 | C001 | S1 | AGL | 510563 |
| 2 | 123 | C001 | S1 | USL | 122967 |
| 3 | 123 | C001 | S1 | VALIC | 614106 |
| 4 | 123 | C001 | S2 | Inforce | 1247636 |
| 5 | 123 | C001 | S2 | NB | 0 |
| 6 | 123 | C001 | S3 | Seriatim Summary | 1247636 |
+---------+---------+----------+----------+------------------+---------+
I am trying to get the results as below,
table2 -
+---------+---------+----------+----------+-------+
| seq_ID | REQ_ID | CALL_ID | Summary | COUNT |
+---------+---------+----------+----------+-------+
| 1 | 123 | C001 | S1_vs_S2 | 0 |
| 2 | 123 | C001 | S2_vs_S3 | 0 |
| 3 | 123 | C001 | S3_vs_s1 | 0 |
+---------+---------+----------+----------+-------+
S1_vs_S2 is the difference between (sum(count) from table1 where sell_id='S1') and (sum(count) from table1 where sell_id='S2')
Below is the code that i am using, But couldn't fetch the results,
INSERT INTO table2 (SEQ_ID, REQ_ID,call_id,summary,count)
SELECT min(seq_id) seq_id
, req_id
, call_id
, S1_vs_S2
,((SELECT sum(c2) FROM TABLE_STG_CTRL WHERE source='S1')-
SELECT sum(c2) FROM TABLE_STG_CTRL WHERE source='S2'))
FROM table1
GROUP BY req_ID, Ctrl_ID, c1, source
ORDER BY SEQ_ID ;
Does this do what you want?
select req_id, call_id, sell_id,
lead(sell_id) over (partition by req_id, call_id order by seq_id) as next_sell_id,
(cnt -
lead(cnt) over (partition by req_id, call_id order by seq_id)
) as diff
from (select req_id, call_id, sell_id, sum(count) as cnt, min(seq_id) as seq_id
from t
group by req_id, call_id, sell_id
) t
At first group data on sell_id, req_id, call_id. This is subquery t in my code. Then self join properly this result and show difference. The only problem is to construct join condition carefully:
demo with your sample data
with t as (
select sell_id sid, req_id, call_id, sum(cnt) cnt
from table1
group by sell_id, req_id, call_id )
select case t1.sid when 'S1' then 1 when 'S2' then 2 when 'S3' then 3 end id,
t1.req_id, t1.call_id, t1.sid||'_vs_'||t2.sid call_id, t1.cnt - t2.cnt diff
from t t1
join t t2 on t1.req_id = t2.req_id
and t1.call_id = t2.call_id
and (t1.sid, t2.sid) in (('S1', 'S2'), ('S2', 'S3'), ('S3', 'S1'))
order by id
BTW count is Oracle reserved word, please avoid such names when naming columns etc.

Partition by multiple columns written in rows from another table

Let's say I have this table:
+------+-------+-------+-------+-------+--------+
| User | Value | Rule1 | Rule2 | Rule3 | Rule4 |
+------+-------+-------+-------+-------+--------+
| 1 | 10 | 20 | 14 | 15 | 22 |
| 2 | 5 | 20 | 7 | 8 | 25 |
+------+-------+-------+-------+-------+--------+
I want to do a partition by query, like that:
SELECT sum(Value) OVER (PARTITION BY Rule1, Rule2)
FROM MY_TABLE
but i don't want to write "Rule1, Rule2". I want to read a table like
+----+-------+
| ID | Rules |
+----+-------+
| 1 | Rule1 |
| 1 | Rule2 |
| 2 | Rule1 |
| 2 | Rule2 |
| 2 | Rule3 |
| 3 | Rule2 |
| 3 | Rule3 |
| 3 | Rule4 |
+----+-------+
So i can write something like that:
SELECT sum(Value) OVER (PARTITION BY "all rules separated by comma where ID = 1")
FROM MY_TABLE
Is it possible? Someone has a better alternative?
Thanks in advance!
Is this what you want?
select t.*,
sum(value) over
(partition by (case when exists (select 1 from tablelikethis tlt where tlt.id = 1 and tlt.rules = 'rule1') then t.rule1 end),
(case when exists (select 1 from tablelikethis tlt where tlt.id = 1 and tlt.rules = 'rule2') then t.rule2 end),
(case when exists (select 1 from tablelikethis tlt where tlt.id = 1 and tlt.rules = 'rule3') then t.rule3 end),
(case when exists (select 1 from tablelikethis tlt where tlt.id = 1 and tlt.rules = 'rule4') then t.rule4 end)
)
from my_table t

Best Hive SQL query for this

i have 2 table something like this. i'm running a hive query and windows function seems pretty limited in hive.
Table dept
id | name |
1 | a |
2 | b |
3 | c |
4 | d |
Table time (build with heavy load query so it's make a very slow process if i need to join to another newly created table time.)
id | date | first | last |
1 | 1992-01-01 | 1 | 1 |
2 | 1993-02-02 | 1 | 2 |
2 | 1993-03-03 | 2 | 1 |
3 | 1993-01-01 | 1 | 3 |
3 | 1994-01-01 | 2 | 2 |
3 | 1995-01-01 | 3 | 1 |
i need to retrieve something like this :
SELECT d.id,d.name,
t.date AS firstdate,
td.date AS lastdate
FROM dbo.dept d LEFT JOIN dbo.time t ON d.id=t.id AND t.first=1
LEFT JOIN time td ON d.id=td.id AND td.last=1
How the most optimized answer ?
GROUP BY operation that will be done in a single map-reduce job
select id
,max(name) as name
,max(case when first = 1 then `date` end) as firstdate
,max(case when last = 1 then `date` end) as lastdate
from (select id
,null as name
,`date`
,first
,last
from time
where first = 1
or last = 1
union all
select id
,name
,null as `date`
,null as first
,null as last
from dept
) t
group by id
;
+----+------+------------+------------+
| id | name | firstdate | lastdate |
+----+------+------------+------------+
| 1 | a | 1992-01-01 | 1992-01-01 |
| 2 | b | 1993-02-02 | 1993-03-03 |
| 3 | c | 1993-01-01 | 1995-01-01 |
| 4 | d | (null) | (null) |
+----+------+------------+------------+
select d.id
,max(d.name) as name
,max(case when t.first = 1 then t.date end) as 'firstdate'
,max(case when t.last = 1 then t.date end) as 'lastdate'
from dept d left join
time t on d.id = t.id
where t.first = 1 or t.last = 1
group by d.id

Update table with ordered values

i need to update a table ordering by price and reassigning the ordered price.
The price and values are grouped by idcategory. Here is an example:
| ID | idcategory | price | value |
| 1 | 1 | 10 | 3 |
| 2 | 1 | 12 | 30 |
| 3 | 1 | 43 | 9 |
| 4 | 1 | 32 | 23 |
| 5 | 2 | 38 | 13 |
| 6 | 2 | 8 | 26 |
| 7 | 2 | 3 | 34 |
| 8 | 2 | 10 | 12 |
. .. .. .. .. .. .. ... ... .. .. .. ..
I need to reorder the table grouping by idcategory reassigning the ordered value to the ordered price like this:
| ID | idcategory | price | value |
| 1 | 1 | 10 | 3 |
| 2 | 1 | 12 | 9 |
| 3 | 1 | 32 | 23 |
| 4 | 1 | 43 | 30 |
| 5 | 2 | 3 | 12 |
| 6 | 2 | 8 | 13 |
| 7 | 2 | 10 | 26 |
| 8 | 2 | 38 | 34 |
.. .. .. .. .. .. .. .. .. ... ..
database is a postgres 9.2.
any idea will be appreciated.
Thanks you and Happy new Year !!!
this is the updated working solution based on GarethD suggestion:
WITH OrderedValues AS
( SELECT Value,
Price,
idcategory,
ROW_NUMBER() OVER(PARTITION BY idcategory ORDER BY Value) AS ValueNum,
ROW_NUMBER() OVER(PARTITION BY idcategory ORDER BY Price) AS PriceNum
FROM T
), OrderedIDs AS
( SELECT ID,
idcategory,
ROW_NUMBER() OVER(PARTITION BY idcategory ORDER BY ID) AS RowNum
FROM T
), NewValues AS
( SELECT i.ID,
v.Value,
p.Price
FROM OrderedIDs i
INNER JOIN OrderedValues v
ON i.RowNum = v.ValueNum
AND i.idcategory = v.idcategory
INNER JOIN OrderedValues p
ON i.RowNum = p.PriceNum
AND i.idcategory = p.idcategory
)
UPDATE T
SET Price = v.Price,
Value = v.Value
FROM NewValues v
WHERE v.ID = T.ID;
SELECT *
FROM T;
You first need to rank your both your IDs (OrderedIDs), and your Price/Value combination (OrderedValues). Then you can matched the corresponding ranks (NewValues), and update your table accordingly:
WITH OrderedValues AS
( SELECT Value,
Price,
idcategory,
ROW_NUMBER() OVER(PARTITION BY idcategory ORDER BY Value, Price) AS RowNum
FROM T
), OrderedIDs AS
( SELECT ID,
idcategory,
ROW_NUMBER() OVER(PARTITION BY idcategory ORDER BY ID) AS RowNum
FROM T
), NewValues AS
( SELECT i.ID,
v.Value,
v.Price
FROM OrderedIDs i
INNER JOIN OrderedValues v
ON i.RowNum = v.RowNum
AND i.idcategory = v.idcategory
)
UPDATE T
SET Price = v.Price,
Value = v.Value
FROM NewValues v
WHERE v.ID = T.ID;
Example on SQL Fiddle