How to concat two fields and use the result in WHERE clause? - sql

I have to get all oldest records based on the date-time information.
Data
Id | External Id | Date | Time
1 | 1000 | 2020-08-18 00:00:00 | 02:30:22
2 | 1000 | 2020-08-12 00:00:00 | 12:45:51
3 | 1556 | 2020-08-17 00:00:00 | 10:09:01
4 | 1919 | 2020-08-14 00:00:00 | 18:19:18
5 | 1919 | 2020-08-14 00:00:00 | 11:45:21
6 | 1919 | 2020-08-14 00:00:00 | 15:54:15
Expected result
Id | External Id | Date | Time
2 | 1000 | 2020-08-12 00:00:00 | 12:45:51
3 | 1556 | 2020-08-17 00:00:00 | 10:09:01
5 | 1919 | 2020-08-14 00:00:00 | 11:45:21
I'm currently doing this
SELECT *
FROM RUN AS T1
WHERE CONCAT(T1.DATE, T1.TIME) = (
SELECT MIN(CONCAT(T2.DATE, T2.TIME))
FROM RUN AS T2
WHERE T2.EXTERNAL_ID = T1.EXTERNAL_ID
)
Is it a correct way to do ?
Thank you, regards
Update 1 : Data type
DATE column is datetime
TIME column is varchar

You can use a window function such as DENSE_RANK()
SELECT ID, External_ID, Date, Time
FROM
(
SELECT DENSE_RANK() OVER (PARTITION BY External_ID ORDER BY Date, Time) AS dr,
r.*
FROM run r
) AS q
WHERE dr = 1
Demo

Related

SQL Server - Counting total number of days user had active contracts

I want to count the number of days while user had active contract based on table with start and end dates for each service contract. I want to count the time of any activity, no matter if the customer had 1 or 5 contracts active at same time.
+---------+-------------+------------+------------+
| USER_ID | CONTRACT_ID | START_DATE | END_DATE |
+---------+-------------+------------+------------+
| 1 | 14 | 18.02.2021 | 18.04.2022 |
| 1 | 13 | 02.01.2019 | 02.01.2020 |
| 1 | 12 | 01.01.2018 | 01.01.2019 |
| 1 | 11 | 13.02.2017 | 13.02.2019 |
| 2 | 23 | 19.06.2021 | 18.04.2022 |
| 2 | 22 | 01.07.2019 | 01.07.2020 |
| 2 | 21 | 19.01.2019 | 19.01.2020 |
+---------+-------------+------------+------------+
In result I want a table:
+---------+--------------------+
| USER_ID | DAYS_BEEING_ACTIVE |
+---------+--------------------+
| 1 | 1477 |
| 2 | 832 |
+---------+--------------------+
Where
1477 stands by 1053 (days from 13.02.2017 to 02.01.2020 - user had active contracts during this time) + 424 (days from 18.02.2021 to 18.04.2022)
832 stands by 529 (days from 19.01.2019 to 01.07.2020) + 303 (days from 19.06.2021 to 18.04.2022).
I tried some queries with joins, datediff's, case when conditions but nothing worked. I'll be grateful for any help.
If you don't have a Tally/Numbers table (highly recommended), you can use an ad-hoc tally/numbers table
Example or dbFiddle
Select User_ID
,Days = count(DISTINCT dateadd(DAY,N,Start_Date))
from YourTable A
Join ( Select Top 10000 N=Row_Number() Over (Order By (Select NULL))
From master..spt_values n1, master..spt_values n2
) B
On N<=DateDiff(DAY,Start_Date,End_Date)
Group By User_ID
Results
User_ID Days
1 1477
2 832

Select first `n` rows of a grouped query

I am using PostgreSQL with SQLAlchemy
I have a table of GPS metrics in the form:
SELECT * FROM user_gps_location;
My Output:
| id | user_id | entry_time | lat | lng | accuracy | altitude | speed |
| 1 | 54 | 2020-07-24 14:08:30.000000 | 54.42184220 | -110.21029370 | 41.42 | 512.40 | 0.07 |
| 2 | 54 | 2020-07-24 22:20:12.000000 | 54.42189750 | -110.21038070 | 13.00 | 512.60 | 0.00 |
| 3 | 26 | 2020-07-27 13:51:11.000000 | 54.41453910 | -110.20775990 | 1300.00 | 0.00 | 0.00 |
| 4 | 26 | 2020-07-27 22:59:00.000000 | 54.42122590 | -110.20959960 | 257.52 | 509.10 | 0.00 |
| 5 | 26 | 2020-07-28 13:54:12.000000 | 54.42185280 | -110.21025010 | 81.45 | 510.20 | 0.00 |
...
I need to be able to answer the question "What are the latest 5 entries for each user since "", sorted by entry_time
Right now I only have a basic query:
select *
from user_gps_location
where user_id in (select distinct user_id
from user_gps_location
where entry_time > '2020-09-01')
and entry_time > '2020-09-01';
Applying a limit will not do what I want. I assume I need to use a grouping and window functions (?), but I do not understand them.
The row_number function is exactly what you're looking for:
SELECT *
FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY entry_time DESC) AS rn
FROM user_gps_location
WHERE entry_time > '2020-09-01') t
WHERE rn <= 5
you can use FETCH FIRST N ROWS ONLY
select * from user_gps_location
where entry_time > '2020-09-01'
order by entry_time desc
fetch first 5 rows only

Where clause changing the results of my datediff column, How can I work around this?

I'm trying to obtain the time elapsed while st1=5. Here is what I currently have, which gives me the datediff time for each state change. My issue is that when i add a where st1=5 clause the datediff shows the difference in time between instances where the state = 5 instead of time elapsed where state is 5.
select timestamp,st1,st2,st3,st4,
datediff(second, timestamp, lead(timestamp)
over (order by timestamp)) as timediff
from A6K_status
Order By Timestamp DESC
+-----+-----+-----+-----+---------------------+----------+
| st1 | st2 | st3 | st4 | TimeStamp | TimeDiff |
+-----+-----+-----+-----+---------------------+----------+
| 3 | 3 | 3 | 3 | 2018-07-23 07:51:06 | |
+-----+-----+-----+-----+---------------------+----------+
| 5 | 5 | 5 | 5 | 2018-07-23 07:50:00 | 66 |
+-----+-----+-----+-----+---------------------+----------+
| 0 | 0 | 10 | 10 | 2018-07-23 07:47:19 | 161 |
+-----+-----+-----+-----+---------------------+----------+
| 5 | 5 | 5 | 5 | 2018-07-23 07:39:07 | 492 |
+-----+-----+-----+-----+---------------------+----------+
| 3 | 3 | 10 | 10 | 2018-07-23 07:37:48 | 79 |
+-----+-----+-----+-----+---------------------+----------+
| 3 | 3 | 10 | 10 | 2018-07-23 07:37:16 | 32 |
+-----+-----+-----+-----+---------------------+----------+
I am trying to sum the time that the state of station1 is 5. From this table above(what I have right now) if i could just sum timediff Where st1=5 that would work perfectly. But by adding "where st1=5" to my query gives me the time difference between instances where the state = 5.
Any help would be much appreciated. I feel very close to the result I would like to achieve. Thanks you.
Edit
This is what I would like to achieve
+-----+------------+----------+
| st1 | TimeStamp | TimeDiff |
+-----+------------+----------+
| 5 | 2018-07-23 | 558 |
+-----+------------+----------+
You would use a subquery (or CTE):
select sum(timediff)
from (select timestamp, st1, st2, st3, st4,
datediff(second, timestamp, lead(timestamp) over (order by timestamp)) as timediff
from A6K_status
) s
where st1 = 5;
Assuming SQL Server, try something like this:
WITH SourceTable AS (
select TOP 100 PERCENT timestamp,st1,st2,st3,st4,
datediff(second, timestamp, lead(timestamp)
over (order by timestamp)) as timediff
from A6K_status
Order By Timestamp DESC
)
SELECT SUM(timediff) as totaltimediff
WHERE st1 = 5

SQL JOIN with multiple date condition

We have the first valuetable table and the query should check if there is
a next younger datetime in the correctiontable table and should add the corrvalue with the corrdatetime.
My problem query:
SELECT * FROM valuetable vt
LEFT JOIN correctiontable corr ON corr.value_id = vt.id WHERE vt.datetime <= corr.corrdatetime
is just delivering the last corrdatetime...
To clarify te results:
Row1 id1 should be NULL as the valuetable datetime is younger than the correction datetime
Row2 id2 should be 01/08/2017 00:00:00 as the datetime in valuetable is older but younger than the 01/12/2017 10:00:00 corrdatetime
Row3 id2 got its correction on 01/12/2017 10:00:00
Row4 id3 is NULL, there is no corrdatetime in correctiontable for it
Thank you all ++
+----------------------------------+
| valuetable |
+----------------------------------+
| id | datetime | value |
+----+---------------------+-------+
| 1 | 22/07/2017 13:00:00 | 123 |
+----+---------------------+-------+
| 2 | 10/08/2017 09:00:00 | 456 |
+----+---------------------+-------+
| 2 | 05/12/2017 20:00:00 | 789 |
+----+---------------------+-------+
| 3 | 11/11/2017 11:11:11 | 012 |
+----+---------------------+-------+
+-------------------------------------------------+
| correctiontable |
+-------------------------------------------------+
| id | value_id | corrdatetime | corrvalue |
+----+----------+---------------------+-----------+
| 1 | 2 | 01/08/2017 00:00:00 | 888 |
+----+----------+---------------------+-----------+
| 2 | 2 | 01/12/2017 10:00:00 | 999 |
+----+----------+---------------------+-----------+
| 3 | 1 | 01/08/2017 20:00:00 | 111 |
+----+----------+---------------------+-----------+
+--------------------------------------------------------------------+
| Result (as it should be) |
+--------------------------------------------------------------------+
| id | datetime | corrdatetime | value | corrvalue |
+----+---------------------+---------------------+-------+-----------+
| 1 | 22/07/2017 13:00:00 | NULL | 123 | NULL |
+----+---------------------+---------------------+-------+-----------+
| 2 | 10/08/2017 09:00:00 | 01/08/2017 00:00:00 | 456 | 888 |
+----+---------------------+---------------------+-------+-----------+
| 2 | 05/12/2017 20:00:00 | 01/12/2017 10:00:00 | 789 | 999 |
+----+---------------------+---------------------+-------+-----------+
| 3 | 11/11/2017 11:11:11 | NULL | 012 | NULL |
+----+---------------------+---------------------+-------+-----------+
Assuming "younger" means "logically less than", this should work for you.
select *
from valuetable a
outer apply (
select top 1 *
from correctiontable y
where y.value_id = a.id
and y.datetime < a.datetime
order by y.datetime desc
) b
Many Thanks to #KindaTechy for delivering me the right path!
I've created two querys, one for MySQL and one for >= Oracle 12.1
For MySQL:
SELECT *
FROM valuetable vt
LEFT JOIN correctiontable ON correctiontable.id
=
(SELECT corr.id
FROM correctiontable corr
WHERE vt.id = corr.value_id
AND vt.datetime <= corr.corrdatetime
ORDER BY datetime DESC
LIMIT 1)
For Oracle:
select *
from valuetable vt
outer apply (
select *
from correctiontable corr
where corr.value_id = vt.id
and corr.corrdatetime < vt.datetime
order by corr.corrdatetime desc
FETCH FIRST 1 ROWS ONLY
) b;
I found a working query, but your id column of valuetable should be unique, because otherwise you get a cross product.
SELECT vt.id, vt.datetime, corr.corrdatetime, vt.value, corr.corrvalue
FROM valuetable vt
LEFT JOIN correctiontable corr
ON corr.value_id = vt.id
AND vt.datetime >= corr.corrdatetime
By changing the date constraint from WHERE-CLAUSE to ON-CLAUSE it will impact only the join and not the result.
A made a sample for you http://sqlfiddle.com/#!9/301a6/4/0
When you need that non-unique id, the query must be improved. And also the test data set.

HOW TO: SQL Server select distinct field based on max value in other field

id tmpname date_used tkt_nr
---|---------|------------------|--------|
1 | template| 04/03/2009 16:10 | 00011 |
2 | templat1| 04/03/2009 16:11 | 00011 |
5 | templat2| 04/03/2009 16:12 | 00011 |
3 | diffname| 03/03/2009 15:11 | 00022 |
4 | diffname| 03/03/2009 16:12 | 00022 |
6 | another | 03/03/2009 16:13 | NULL |
7 | somethin| 24/12/2008 11:12 | 00023 |
8 | name | 01/01/2009 12:12 | 00026 |
I would like to have the result:
id tmpname date_used tkt_nr
---|---------|------------------|--------|
5 | templat2| 04/03/2009 16:12 | 00011 |
4 | diffname| 03/03/2009 16:12 | 00022 |
7 | somethin| 24/12/2008 11:12 | 00023 |
8 | name | 01/01/2009 12:12 | 00026 |
So what I'm looking for is to have distinct tkt_nr values excluding NULL, based on the max value of datetime.
I have tried several options but always failed
SELECT *
FROM templateFeedback a
JOIN (
SELECT ticket_number, MAX(date_used) date_used
FROM templateFeedback
GROUP BY ticket_number
) b
ON a.ticket_number = b.ticket_number AND a.date_used = b.date_used
I would appreciate any help. Unfortunately I need the code to be compatible with SQL Server.
I've stopped doing things this way since I discovered windowing functions. Too often, there are two records with the same timestamp and I get two records in the resultset. Here's the code for tSQL. Similar for Oracle. I don't think mySQL supports this yet.
Select id, tmpname, date_used, tkt_nbr
From
(
Select id, tmpname, date_used, tkt_nbr,
rownum = Row_Number() Over (Partition by tkt_nbr Order by date_used desc)
) x
Where row_num=1