Get specific value from each group after partition - sql

I have below table -
ID | Type | Val0 | Val1 | Group | Creation_date
1 | A | V1 | Val1 | G1 | 24sep, 2011
2 | A | V1 | Val2 | G1 | 27oct, 2008
3 | A | V2 | Val3 | G2 | 19oct, 2003
4 | B | X1 | Xal1 | G3 | 15 dec, 2001
5 | B | X2 | Xal2 | G4 | 15 dec, 2000
6 | B | X2 | Xal3 | G4 | 15 dec, 1990
7 | C | X3 | Xal4 | G5 | 23Dec, 2001
I want to keep only the latest values for a particular type and extract out values which are old but not belong to group with which the latest item is associated. So, for above table, I want these items to be extracted out - Val3, Xal2, Xal3 which i can delete later.
since A is having the latest value as Val1 which belongs to group G1, B is having the latest value as Xal1 and C is having the latest value as Xal4.
Can anybody suggest me how i can do this using SQL?

You can use row_number(). No aggregation is necessary:
select t.*
from (select t.*,
row_number() over (partition by t.type order by creation_date desc) as seqnum
from t
) t
where seqnum = 1;
However, you want the values that are not in this group. That is, you want the values from val1 where seqnum is not 1. This would be:
select t.val1
from (select t.*, min(seqnum) over (partition by t.type, t.group) as min_seqnum -- DON'T USE group FOR COLUMN NAMES!
from (select t.*,
row_number() over (partition by t.type order by creation_date desc) as seqnum
from t
) t
) t
where min_seqnum <> 1;

You can use ROW_NUMBER() :
SELECT p.type,p.val1 FROM (
SELECT t.*,
ROW_NUMBER() OVER(PARTITION BY t.type ORDER BY t.max_d DESC) as rnk
FROM (SELECT s.Type,s.Val1,MAX(s.creation_date) as max_d
FROM YourTable s
GROUP BY s.type,s.val1) t) p
WHERE p.rnk > 1

Related

How to use pivot to select and flatten table?

I'm trying to select from a table that essentially is a hierarchy of groups and fields in each group. Each row has a group id column and I'm trying to flatten it into rows of each group id and their fields.
For example
group id | field1
1 | a
1 | b
1 | a
1 | b
2 | c
2 | d
2 | c
2 | d
3 | e
3 | f
3 | g
3 | e
3 | f
3 | g
4 | h
It is guaranteed that a group will map to the same fields values so group 1 will always have the same number of rows with field 'a' as with field 'b'.
The target is this:
group id | field1 | field2 | field 3
1 | a | b | null
2 | c | d | null
3 | e | f | g
4 | h | null | null
I have been playing with over (order by group id) but I haven't made any progress with that or pivots either.
I wouldn't use pivot. I would use conditional aggregation and dense_rank():
select group_id,
max(case when seqnum = 1 then field1 end) as field1,
max(case when seqnum = 2 then field1 end) as field2,
max(case when seqnum = 3 then field1 end) as field3
from (select t.*,
dense_rank() over (partition by group_id order by field1) as seqnum
from t
) t
group by group_id
Not sure this will solve your problem. If you are generating any report then you can use LISTAGG function.
select listagg( field_1 , ',') within group (order by group_id)
from (
select distinct group_id, field_1 from table
);

SQL Server Add row number each group

I working on a query for SQL Server 2016. I have order by serial_no and group by pay_type and I would like to add row number same example below
row_no | pay_type | serial_no
1 | A | 4000118445
2 | A | 4000118458
3 | A | 4000118461
4 | A | 4000118473
5 | A | 4000118486
1 | B | 4000118499
2 | B | 4000118506
3 | B | 4000118519
4 | B | 4000118521
1 | A | 4000118534
2 | A | 4000118547
3 | A | 4000118550
1 | B | 4000118562
2 | B | 4000118565
3 | B | 4000118570
4 | B | 4000118572
Help me please..
SELECT
ROW_NUMBER() OVER(PARTITION BY paytype ORDER BY serial_no) as row_no,
paytype, serial_no
FROM table
ORDER BY serial_no
You can assign groups to adjacent pay types that are the same and then use row_number(). For this purpose, the difference of row numbers is a good way to determine the groups:
select row_number() over (partition by pay_type, seqnum - seqnum_2 order by serial_no) as row_no,
t.*
from (select t.*,
row_number() over (order by serial_no) as seqnum,
row_number() over (partition by pay_type order by serial_no) as seqnum_2
from t
) t;
This type of problem is one example of a gaps-and-islands problem. Why does the difference of row numbers work? I find that the simplest way to understand is to look at the results of the subquery.
Here is a db<>fiddle.
add this to your select list
ROW_NUMBER() OVER ( ORDER BY (SELECT 1) )
since you already sorting by your stuff, so you don't need to sorting in your windowing function so consuming less CPU,

How to list the latest series with no gaps of a given clause?

Given the following example table:
+-----------+
| Id | Name |
+----+------+
| 1 | A |
| 2 | B |
| 3 | B |
| 4 | C |
| 5 | A |
| 6 | B |
| 7 | B |
| 8 | B |
| 9 | B |
| 10 | X |
+----+------+
I would like a query to get the following result:
+----+------+
| 6 | B |
| 7 | B |
| 8 | B |
| 9 | B |
+----+------+
The best query I could do was:
SELECT * FROM
(SELECT id, name, LEAD(id) OVER (ORDER BY id) t
FROM test WHERE name = 'B' ORDER BY id)
WHERE ID <> t-1;
sqlfiddle here
If you want the length and where it starts:
select min(id), max(id)
from (select t.*,
row_number() over (order by id) as seqnum,
row_number() over (partition by name order by id) as seqnum_1
from test t
) t
where name = 'B'
group by (seqnum - seqnum_1)
order by min(id) desc
fetch first 1 row only;
You can join back to the table to get the original rows.
Another method using window functions to count the number of non-Bs after a given row . . . and then choose the first:
select t.*
from (select t.*,
dense_rank() over (order by nonbs_after asc) as grp
from (select t.*,
sum(case when name <> 'B' then 1 else 0 end) over (order by id desc) as nonbs_after
from test t
) t
where name = 'B'
) t
where grp = 1;
Here is a db<>fiddle.

SQL query for selecting multiple records for one product for a single id

My table looks like this, what I'm trying to achieve is to pull out all the records for one user for the product that have the earliest date
product |type_id| user | Date |Desired ROW_NUMBER as output |
-------+--------+------+-------+---------------------
1 | 1 | A | 0101 | 1
1 | 1 | A | 0102 | 1
2 | 3 | A | 0105 | 2
2 | 5 | A | 0105 | 2
3 | 7 | B | 0101 | 1
3 | 8 | B | 0104 | 1
So I want to pull all the records with "1" in the desired row_num column, but I haven't figured out hot to get this without doing another group by. Any helps would be appreciated.
You can use window functions:
select t.*
from (select t.*,
rank() over (partition by user order by min_date) as seqnum
from (select t.*,
min(date) over (partition by user, product) as min_date
from t
) t
) t
where seqnum = 1;
Or, with only one subquery:
select t.*
from (select t.*,
min(date) over (partition by user, product) as min_date_up,
min(date) over (partition by user) as min_date_u
from t
) t
where min_date_u = min_date_up;
You can interpret this as "return all rows where the product has the minimum date for the user".
Here is a db<>fiddle.
SELECT * FROM [tableName] WHERE Desired ROW_NUMBER = 1 ORDER BY Date[DESC, ASC]
Pass the Desired ROW_NUMBER value dynamically as a parameter.

Shuffle data in Sqlite

I have a data like
id1,apple,0
id2,orange,0
id3,banana,0
id4,carrot,0
ida,kiwi,1
idb,potato,1
idc,cakes,1
idd,chocos,1
I need to shuffle on the base of last column (0 THEN 1) like
id1,apple,0
ida,kiwi,1
id2,orange,0
idb,potato,1
id3,banana,0
idc,cakes,1
id4,carrot,0
idd,chocos,1
Is that possible in sqlite or in notepad++ ??
If the version of SQLite you use supports it you can do it with row_number() window function:
select t.id, t.fruit, t.number
from (
select *,
row_number() over (partition by number order by id) rn
from tablename
) t
order by t.rn
If you need the rows shuffled, replace order by id with order by random().
See the demo.
If you can't use window functions:
select t.id, t.fruit, t.number
from (
select t.*,
(select count(*) from tablename where number = t.number and id < t.id) rn
from tablename t
) t
order by t.rn
See the demo.
Results:
| id | fruit | number |
| --- | ------ | ------ |
| id1 | apple | 0 |
| ida | kiwi | 1 |
| id2 | orange | 0 |
| idb | potato | 1 |
| id3 | banana | 0 |
| idc | cakes | 1 |
| id4 | carrot | 0 |
| idd | chocos | 1 |
You can do this using row_number() in the order by clause:
select t.*
from t
order by row_number() over (partition by col3 order by col1),
col1;
Often "shuffle" implies randomness ("interleaving" would not). If that is what you mean:
select t.*
from t
order by row_number() over (partition by col3 order by random()),
col1