select first and last record of each group horizontally - sql

I have a table like
i want to select first and last record of every group by facility_id and created_at horizontally
need to output like . i can do it vertically but need horizontally

with CTE as (
select
*
,ROW_NUMBER() over (partition by facility_id,name order by created_at asc ) ascrnk
,ROW_NUMBER() over (partition by facility_id,name order by created_at desc ) desrnk
from TestTable
)
select T1.facility_id,T1.name,
T1.value as "First_value",
T1.created_at as "First created_at",
T2.value as "Last_value",
T2.created_at as "Last created_at"
from (
select * from cte
where ascrnk = 1
) T1
left join (
select * from cte
where desrnk = 1
) T2 on T1.facility_id = T2.facility_id and T1.name = T2.name
Result:
| facility_id | name | First_value | First created_at | Last_value | Last created_at |
|-------------|------|-------------|----------------------|------------|----------------------|
| 2011 | A | 200 | 2015-05-30T11:50:17Z | 300 | 2017-05-30T11:50:17Z |
| 2012 | B | 124 | 2015-05-30T11:50:17Z | 195 | 2017-05-30T11:50:17Z |
| 2013 | C | 231 | 2015-05-30T11:50:17Z | 275 | 2017-06-30T11:50:17Z |
| 2014 | D | 279 | 2017-06-30T11:50:17Z | 263 | 2018-06-30T11:50:17Z |
SQL Fiddle Demo Link

I think this is much simpler using window functions and select distinct:
select distinct facility_id, name,
first_value(value) over (partition by facility_id, name order by created_at asc) as first_value,
min(created_at) as first_created_at,
first_value(value) over (partition by facility_id, name order by created_at desc) as last_value,
max(created_at) as last_created_at
from t;
No subqueries. No joins.
You can also use arrays to accomplish the same functionality, using group by. It is a shame that SQL Server doesn't directly support first_value() as a window function.

Related

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,

PostgreSQL: Filter select query by comparing against other rows

Suppose I have a table of Events that lists a userId and the time the Event occurred:
+----+--------+----------------------------+
| id | userId | time |
+----+--------+----------------------------+
| 1 | 46 | 2020-07-22 11:22:55.307+00 |
| 2 | 190 | 2020-07-13 20:57:07.138+00 |
| 3 | 17 | 2020-07-11 11:33:21.919+00 |
| 4 | 46 | 2020-07-22 10:17:11.104+00 |
| 5 | 97 | 2020-07-13 20:57:07.138+00 |
| 6 | 17 | 2020-07-04 11:33:21.919+00 |
| 6 | 17 | 2020-07-11 09:23:21.919+00 |
+----+--------+----------------------------+
I want to get the list of events that had a previous event on the same day, by the same user. The result for the above table would be:
+----+--------+----------------------------+
| id | userId | time |
+----+--------+----------------------------+
| 1 | 46 | 2020-07-22 11:22:55.307+00 |
| 3 | 17 | 2020-07-11 11:33:21.919+00 |
+----+--------+----------------------------+
How can I perform a select query that filters results by evaluating them against other rows in the table?
This can be done using an EXISTS condition:
select t1.*
from the_table t1
where exists (select *
from the_table t2
where t2.userid = t1.userid -- for the same user
and t2.time::date = t1.time::date -- on the same
and t2.time < t1.time); -- but previously on that day
You can use lag():
select t.*
from (select t.*,
lag(time) over (partition by userid, time::date order by time) as prev_time
from t
) t
where prev_time is not null;
Here is a db<>fiddle.
Or row_number():
select t.*
from (select t.*,
row_number() over (partition by userid, time::date order by time) as seqnum
from t
) t
where seqnum >= 2;
You can use LAG() to find the previous row for a user. Then a simple comparison will tell if it occured in the same day or not.
For example:
select *
from (
select
*,
lag(time) over(partition by userId order by time) as prev_time
from t
) x
where date::date = prev_time::date
You can use ROW_NUMBER() analytic function :
SELECT id , userId , time
FROM
(
SELECT ROW_NUMBER() OVER (PARTITION BY UserId, date_trunc('day',time) ORDER BY time DESC) AS rn,
t.*
FROM Events
) q
WHERE rn > 1
in order to bring the latest event for UserId who takes place in more than one event.

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

Select top 1 Student Fee From List In SQL Server

In my SQL Server table, I have this data:
+------+-----+------------+
| Name | Fee | Date_Time |
+------+-----+------------+
| AA | 50 | 2018-03-27 |
| AA | 30 | 2018-04-10 |
| BB | 40 | 2018-01-10 |
| BB | 10 | 2018-04-10 |
| CC | 10 | 2018-04-10 |
| DD | 10 | 2018-04-10 |
+------+-----+------------+
How can I get data using SQL query like TOP 1 for (AA, BB, CC, DD) ORDER BY Date_Time DESC into a list?
+------+-----+------------+
| Name | Fee | Date_Time |
+------+-----+------------+
| AA | 30 | 2018-04-10 |
| BB | 10 | 2018-04-10 |
| CC | 10 | 2018-04-10 |
| DD | 10 | 2018-04-10 |
+------+-----+------------+
Use row_number() function to get the top most Fee
select top(1) with ties Name, Fee, Date_Time
from table t
order by row_number() over (partition by Name order by Date_Time desc)
Another approach can be
SELECT Name,Fee,Date_Time FROM
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY NAME ORDER BY DATE_TIME DESC) RN
FROM [TABLE_NAME]
) T
WHERE RN=1
In case if you have multiple entries on same day for a particular fee, and you want both should appear you can use DENSE_RANK() instead of ROW_NUMBER() like following.
SELECT Name,Fee,Date_Time FROM
(
SELECT *, DENSE_RANK() OVER(PARTITION BY NAME ORDER BY DATE_TIME DESC) RN
FROM [TABLE_NAME]
) T
WHERE RN=1
DEMO
Give a row_number based on the partition by Name and order by descending order of Date_Time and then select rows having row_number is 1.
Query
;with cte as (
select [rn] = row_number() over(
partition by [Name]
order by [Date_Time] desc
), *
from [your_table_name]
)
select [Name], [Fee], [Date_Time]
from cte
where [rn] = 1;