TSQL Querying ONLY rows that appeared in specified multiple dates? - sql

So say we have rows
ID | Date
1 1/20
2 1/20
1 1/21
7 1/21
4 1/22
5 1/22
So say I only want to see an ID that appeared in BOTH 1/20 and 1/21.
This should only give me ID 1 since that is the only rows that appears in 1/20 and 1/21. What s the simplest way to achieve this?
I tried doing this:
Select ID, [date]
FROM t1
INNER JOIN (
SELECT ID, Count(*) countRow
FROM t1
Where [date] in(1/20, 1/21)
GROUP BY ID
having count(DISTINCT [Date]) > 1
) aa on t1.id = aa.id
I feel like there's a simple way to achieve this. Any thoughts?

The way you have now is pretty simple. An alternative using exists():
select
t.id
, t.date
from t
where t.date in (1/20, 1/21)
and exists (
select 1
from t as i
where i.id = t.id
and i.date <> t.date
and i.date in (1/20, 1/21)
)

One method uses group by and having:
select id
from t1
where date in ('1/20', '1/21')
group by id
having count(distinct date) = 2;
Of course, if date is really stored as a date, you should fix the format for the date constant to by YYYY-MM-DD.

SELECT ID FROM T1 WHERE Date = '1/20' AND EXISTS (
SELECT ID FROM T1 AS T2 WHERE T2.Date = '1/21' AND
T1.ID = T2.ID)

Not sure it is better
The way you are doing it is pretty standard
Not sure why you need to list the date when that is you where condition
If ID, date is unique then you can use count(*)
Select ID, [date]
FROM t1
INNER JOIN ( SELECT ID
FROM t1
Where [date] = 1/20
intersection
SELECT ID
FROM t1
Where [date] = 1/21
) aa
on t1.id = aa.id
Select ID, [date]
FROM t1
INNER JOIN ( SELECT ID
FROM t1 t1a
join t1 t1b
on t1a.ID = t1b.ID
and t1a.[date] = 1/20
and t1b.[date] = 1/21
) aa
on t1.id = aa.id
If id, value is unique
select id, value
from (select id, value
, count(*) over (partition by id order by value) as cnt
from t
where value in ('a', 'b')
) td
where cnt = 2
order by id, value

Related

SQL query to get fields from 2nd table based on input from 1st

I have 2 tables, table1 and table2
table1 -
sn
timestamp
123
123456
table2 -
sn
timestamp
code
456
123456
xxxxx
I want to first get the sn from table1 with max timestamp(epoch) and then query table 2 with the sn returned from table1 and get the code from table2 with the max timestamp.
I am trying this but getting errors -
select code from table2 where sn = (select sn, max(timestamp) from table1 GROUP BY sn)
Should i use joins instead?
To get the "latest" value I always use windowing functions.
In order to keep everything clean, I've used two CTEs to define the two datasets but you can put everything in a single query if needed.
WITH latestDataT1 AS (
SELECT
sn
,timestamp
FROM (
SELECT
sn
,timestamp
,ROW_NUMBER() OVER(ORDER BY timestamp DESC) as RowNo
FROM table1
)
WHERE
RowNo = 1
)
WITH latestDataT2 AS (
SELECT
sn
,timestamp
,code
FROM (
SELECT
sn
,timestamp
,code
,ROW_NUMBER() OVER(PARTITION sn BY ORDER BY timestamp DESC) as RowNo
FROM table2
)
WHERE
RowNo = 1
)
SELECT
*
FROM latestDataT1 AS t1
LEFT JOIN latestDataT2 AS t2
ON t1.sn = t2.sn
Can you try one of these scripts:
1)
select code
from table2
where (sn, timestamp) in (select sn, max(timestamp) from table1 GROUP BY sn);
2)
select A.code, B.maxtime
from table2 A,
(select sn, max(timestamp) as maxtime
from table1 GROUP BY sn) B
where A.sn = B.sn
and A.timestamp = B.maxtime;
Thank you
Try this:
select DISTINCT code from table2
where sn = (select sn from table1 WHERE timestamp = (SELECT MAX(timestamp) FROM Table1));
You can do this using the following code:
SELECT code
from table2 t
where sn = (SELECT sn
FROM table1 t1
INNER JOIN (SELECT MAX(timestamp) AS max_ts
FROM table1) d
ON t1.timestamp = d.max_ts
)
INNER JOIN (SELECT MAX(timestamp) AS max_ts
FROM table2) t2
ON t.timestamp= t2.max_ts;
However using the example tables you provided this will return no results, as the sn 123 isn't found in table2

Postgres: how to join closest value from the same table

I have a the following table
CREATE TABLE temp (
id SERIAL,
other_id INTEGER NOT NULL, -- some ForeignKey
date DATE NOT NULL
)
I want to join this table to itself by previous (closest) date item with the same other_id. Something like
SELECT count(*)
FROM temp AS t1
JOIN temp AS t2 ON (t2.other_id = t1.other_id AND t2.date < t1.date)
But t2.date must be closest to t1.date (not any lower date).
Is that possible at all?
You can use a query like the following:
WITH temp_rn AS (
SELECT id, other_id, date,
ROW_NUMBER() OVER (PARTITION BY other_id
ORDER BY date) AS rn
FROM temp
)
SELECT t1.*
FROM temp_rn AS t1
LEFT JOIN temp_rn AS t2 ON t1.other_id = t2.other_id AND t1.rn = t2.rn + 1
The query uses ROW_NUMBER in order to detect the 'previous' row: it is the one having the previous row number within the same other_id slice.
It's not entirely clear what you are after, but something like this might do it:
select count(*)
from temp as t1
join lateral (
select t.other_id, max(t.date)
from temp as t
where t.date < t1.date
and t.other_id = t1.other_id
group by t2.other_id
) as t2 on t2.other_id = t1.other_id;

SQL Server : Query To Retrieve a Row based on Value in a Column

I have two databases one database is recording tank levels every 1 minute. We have about 30 tanks. The other database contains lookup tables that contain the number of gallons or each tank for a given level.
I have manged to use an INNER JOIN to get data from the lookup table in SQL Server 2012
WITH T1 As
(
SELECT
DateTime, TagName, Value
FROM
INSQL.Runtime.dbo.History
WHERE
DateTime = (SELECT Max(DateTime)
FROM INSQL.Runtime.dbo.History
WHERE TagName LIKE 'LT%')
AND TagName LIKE 'LT%' AND Value <> '0'
),
T2 AS
(
SELECT *
FROM TankSTrappingDB.dbo.StrappingTable
)
SELECT *
FROM T1
LEFT JOIN T2 ON T1.TagName = T2.TagName
WHERE T2.LIT_PV <= T1.Value
However this returns all of the rows from table 2, where the value of the actual level in the tank is less than the value recorded in table 1. I just need the single row from table 2 that is the closest match to the value recorded in table with with the corresponding timestamp.
Instead of joining T2 to T2, you could lookup the MAX(LIT_PV) in T2, where the TAGname matches? But, if you also want other values from the T2 table, you can add another test to the last bit in your SQL
... ...
SELECT *
FROM T1
LEFT JOIN T2 ON T1.TagName = T2.TagName
WHERE T2.LIT_PV <= T1.Value
and T2.LIT_PV in
(select max(T2A.LIT_PV) from T2 T2A
where T1.TagName = T2A.TagName and T2A.LIT_PV <= T1.Value
)
Please try below query. As the sample data is not available I could not test this query.
WITH T1 As
(
SELECT
DateTime, TagName, Value,
ROW_NUMBER() OVER (PARTITION BY TagName ORDER BY DateTime DESC) RN
FROM
INSQL.Runtime.dbo.History
WHERE
TagName LIKE 'LT%' AND Value <> '0'
),
T2 AS
(
SELECT *,
ROW_NUMBER() OVER(ORDER BY T.LIT_PV DESC) RN
FROM TankSTrappingDB.dbo.StrappingTable T
INNER JOIN T1 ON T1.TagName = T.TagName
AND T.LIT_PV <= T1.Value
WHERE T1.RN = 1
)
SELECT *
FROM T1
INNER JOIN T2 ON T1.TagName = T2.TagName
AND T1.RN = T2.RN
WHERE T2.RN = 1

tsql: alternative to select subquery in join

this is my table layout simplified:
table1: pID (pkey), data
table2: rowID (pkey), pID (fkey), data, date
I want to select some rows from table1 joining one row from table2 per pID for the most recent date for that pID.
I currently do this with the following query:
SELECT * FROM table1 as a
LEFT JOIN table2 AS b ON b.rowID = (SELECT TOP(1) rowID FROM table2 WHERE pID = a.pID ORDER BY date DESC)
This way of working is slow, probabaly because it has to do a subquery on each row of table 1. Is there a way to improve performance on this or do it another way?
You can try something on these lines, use the subquery to get the latest based on the date field (grouping by the pID), then join that with the first table, this way the subquery would not have not have to be executed for each row of Table1 and will result in better performance:
Select *
FROM Table1 a
INNER JOIN
(
SELECT pID, Max(Date) FROM Table2
GROUP BY pID
) b
ON a.pID = b.pID
I have provided the sample SQL for one column using the group by, in case you need additional columns, add them to the GROUP BY clause. Hope this helps.
use the below code, and note that i added the order by Date desc to get the most resent data
select *
from table1 a
inner join table2 b on a.pID=b.pID
where b.rowID in(select top(1) from table2 t where t.pID=a.pID order by Date desc)
I am using the code below in a similar scenaro (I transcripted it to your example)
SELECT b.*
FROM table1 AS a
left outer join (
SELECT a.*
FROM table2 a
inner join (
SELECT a.pID, max(date) as date
FROM table2
WHERE date <= <max_date>
group by pID
) b ON a.pID = b.pID AND a.date = b.date
) b ON a.pID = b.pID
) b on a.pID = b.pID
The only problem with this aproach is that you have to make sure the date's don't reapet for the pID's
You can do this with the row_number() function and a subquery:
SELECT t1.*
FROM table1 t1 LEFT JOIN
(select t2.*, row_number() over (partition by pId order by rowId desc) as seqnum
from table2 t2
) t2
on t1.pId = t2.pId and t2.seqnum = 1;
Use the ROW_NUMBER() function to get a column saying which id of each row in table 2 is the first (As partitioned by the pID, and ordered by the rowDate descending)
Example:
WITH cte AS
(
SELECT
rowID AS t2RowId,
ROW_NUMBER OVER (PARTITION BY pID ORDER BY rowDate DESC) AS rowNum
FROM table2 t2
) -- gets the t2RowIds + a column which says which is the latest for each pID
SELECT t1.*, t2.*
FROM table1 t1
LEFT JOIN
(
table2 t2
JOIN cte ON t2.rowID = cte.t2RowId AND cte.rowNum = 1
) ON t1.pID = t2.pID
This is guaranteed to only return 1 item from table2 per pID, even if multiple items have the same date. You should of course ensure that the date column is indexed in table 2 for quick performance (ideally an index that also covers the PrimaryID of table2)

SQL Select row from table with some minimum value from another table

I suppose this is not so hard but I can not get it.
For example I have table T1:
ID
-----
1000
1001
And I have table T2:
ID GROUP DATE
--------------------------
1000 ADSL 2.2.2012
1000 null 3.2.2012
1000 NOC 4.2.2012
1001 NOC 5.2.2012
1001 null 6.2.2012
1001 TV 7.2.2012
I want to select from T1 only the row that has as GROUP value NOC from T2 but only if NOC group is for the minimum DATE value in T2.
So my result in this case would be only 1001 because for its minimum DATE 5.2.2012 Group is NOC!
I do not want any joins and I can not use default values for IDs (where id=1000 or id=1001) because this is just example of some big table.
Important also is that I can not use t1.id = t2.id because in some application where I am using this I can not write the whole SQL expression but only partial. I can only use id.
I tried something like:
select id
from t1
where
id in (select id from t2
where group = 'NOC'
and date in (select min(date) from t2
where id in (select id from t1)
)
)
But this does not work.
I know it seems little confusing but I really can't use where t1.id = t2.id
Thanks
If T2.ID is a foreign key referencing T1.ID, you don't really need the T1 table, because all the IDs could be obtained from T2 only:
SELECT o.ID
FROM T2 AS o
WHERE EXISTS (
SELECT MIN(i.DATE)
FROM T2 AS i
WHERE i.ID = o.ID
HAVING MIN(i.DATE) = o.DATE
)
WHERE o."GROUP" = 'NOC'
But if you insist on involving T1, you just need to modify the above like this:
SELECT *
FROM T1
WHERE ID IN (
SELECT o.ID
FROM T2 AS o
WHERE o."GROUP" = 'NOC'
AND EXISTS (
SELECT MIN(i.DATE)
FROM T2 AS i
WHERE i.ID = o.ID
HAVING MIN(i.DATE) = o.DATE
)
)
Can you do this in multiple steps?
First of all, to get the minimum date per id, you would need:
select id, peoplegroup, min(date)
from t2
group by id
That will give you
1000 ADSL 2.2.2012
1001 NOC 5.2.2012
Call this table t3.
Then do
select id
from t3
where id in (
select id from t1
)
Try this:
select id from t1 where id in
(select id from t2 where group = 'NOC' and date =
(select min(date) from t2 where id = t1.id))