SQL query which checks, for each row having a date range in t1, whether there exists any recorded date in t2 within that date range? - sql

If I have two tables like so:
Table 1
Start | END | More columns ...
------------------------------------------
2019-10-20 | 2019-10-21 |...
Table 2
Log ID | DATE
--------------
1 | 2019-10-20
2 | 2019-10-22
I've tried to use CASE WHEN, Boolean, Exists but I suspect my logic is wrong somewhere.
I want to return a results table which includes all of the columns from Table 1, with an additional column containing a Boolean value of whether a date within the range for that row exists in the second table.
So the result set should then look like:
Start | End | MoreCols | Available
----------------------------------------------
2019-10-20 | 2019-10-21 | … | True

Use exists:
select t1.*,
(case when exists (select 1
from table2 t2
where t2.date between t1.start and t1.end
)
then 'True' else 'False'
end) as available_flag
from table1 t1
If your database supports boolean types, then you can write this as:
select t1.*,
(exists (select 1
from table2 t2
where t2.date between t1.start and t1.end
)
) as available_flag
from table1 t1

Related

Find the nearest future or equal to date from a table of dates with sql stmt

I have two tables
Table 1
ID | T1_Date
---+-------------
1 | 09/08/2020
2 | 09/30/2020
Table 2
T2_Date | Label
-----------+-----
08/31/2020 | Aug-20
09/20/2020 | Sep-20
10/25/2020 | Oct-20
I'm trying to have the result link the nearest future date label from table 2 with each record in table 1. So my output would look like:
ID | T1_Date | Label
---+------------+--------
1 | 09/08/2020 | Sep-20
2 | 09/30/2020 | Oct-20
So far I can only return all the records that are greater than the T1_Date value, so it repeats all the labels.
Is there a way to just grab the nearest future date label or the equal to label?
One method is a correlated subquery:
select t1.*,
(select max(t2.label) keep (dense_rank first t2.date asc)
from t2
where t2.date > t1.date
)
from t1;
there are many ways, you can solve this problem
One way which is pretty simple but not optimal:
select
t1.*
,(select Label from table2 where t2_date=(select MIN(T2_Date)from table2
where T2_Date>=T1_Date))Label
from table1 t1
-----------------------------------------------------------
The second Way:
with TempTable as
(
select T1_Date,min(T2_Date)T2_Date
from table2
left join table1 on T2_Date>=T1_Date
group by T1_Date
)
select t1.T1_Date,t2.Label from TempTable temp
left join table1 t1 on t1.T1_Date=temp.T1_Date
left join table2 t2 on t2.T2_Date=temp.T2_Date

Take data from two tables and show in one row without duplicates with a where condition

I want to take the data from two tables and output them in one row .
output will have two columns "to" and "from" where the condition is "from" will be having data from second table where type is true and "to" column will have data from second table where type is false . FK_ID in second table is linked to ID on the first table . Please help with the query.
I was trying to do with inner joins and union was not able to make it work . Thanks in advance .
TABLE 1
ID | PATH|
1 | ABC |
2 | EFG |
TABLE 2
ID | FK_ID | NUMBER | TYPE
20 | 1 | 123 | TRUE
21 | 1 | 456 | FALSE
28 | 2 | 888 | FALSE
29 | 2 | 939 | TRUE
OUTPUT SHOULD BE:
ID | PATH | TO | FROM
1 | ABC | 456 | 123
2 | EFG | 888 | 939
Use aggregation with pivoting logic to identify the "to" and "from" components of each path:
SELECT
t1.ID,
t1.PATH,
MAX(CASE WHEN t2.TYPE = 'FALSE' THEN t2.NUMBER END) AS "TO",
MAX(CASE WHEN t2.TYPE = 'TRUE' THEN t2.NUMBER END) AS "FROM"
FROM table1 t1
LEFT JOIN table2 t2
ON t1.ID = t2.FK_ID
GROUP BY
t1.ID,
t1.PATH
ORDER BY
t1.ID;
If performance is an issue, you might find a lateral join to be faster:
SELECT t1.*, t2.*
FROM table1 t1 LEFT JOIN LATERAL
(SELECT SUM(T2.NUMBER) FILTER (WHERE NOT t2.TYPE) as num_to,
SUM(T2.NUMBER) FILTER (WHERE t2.TYPE) as num_from
FROM table2 t2
WHERE t1.ID = t2.FK_ID
) t2
ORDER BY t1.ID;
This avoids the outer GROUP BY and probably the sorting as well (assuming that ID is the primary key).
It also assumes that TYPE is a Postgres boolean type. If not, use string comparisons for the WHERE clauses.

Update a table based on a condition

I have a column Table1.Tradedate and another column Table2.SettlementDate.
Based on the comparison between these 2, I want to update a column in table 2
IF (Table1.TradeDate <= Table2.SettlementDate)
BEGIN
UPDATE Table2 SET Status='Y'
END
This is what I have tried but I know its wrong, since the table will obviously contain more than 1 records. So, I believe what I should do is
use a join on 2 tables based on some #id to pick a particular record
check the IF condition for that particular record
update the Status column in table2.
I hope my approach is correct but I am writing it incorrectly.
Table1:
SKacc | Name | TradeDate | Othercolumns....
1 | xxx | 01/07/2019 |
2 | xxx | 01/06/2019 |
Table2:
SKAcc | Name | SettlementDate | Status |Other Columns....
1 | xxx | 01/08/2019 | NULL |
2 | xxx | 01/08/2019 | NULL |
Try below
update t2 set Status = 'Y'
from table2 t2
join table1 t1 on t1.id = t2.id
where t1.tradeDate <= t2.settlementDate
Try joining the two tables with the related column and then update the table you want to update with the value. Using inner join in the example but can change depending on the usecase
UPDATE Table2
SET Status = 'Y'
FROM Table2
INNER JOIN Table1 ON Table1.id = Table2.table1_id
WHERE Table1.TradeDate <= Table2.SettlementDate
I would not recommend a JOIN for this purpose. Instead:
update table2
set Status = 'Y'
where exists (select 1
from table1 t1
where t1.id = t2.id and
t1.tradeDate <= t2.settlementDate
);
The reason I recommend this version is because you have not specified that id is unique in table1. In general, you only want to use JOIN in UPDATE when you can guarantee that there is only one matching row.

MS SQL Where one column is x or y and both returned

I have a table as follows with dates in. The table has many more records but simplified for asking purposes:
Name | Date | Grade
Person 1 | 01-01-2001 | B
Person 1 | 31-01-2001 | A
Person 2 | 01-01-2001 | C
Person 3 | 31-01-2001 | A
I want to return both records for Person 1 but not either of the other two. AND returns nothing obviously and OR returns everything. I want to search on the date not the grade or the person.
So the result would be:
Name | Date | Grade
Person 1 | 01-01-2001 | B
Person 1 | 31-01-2001 | A
One simple way to handle this is to aggregate by person and then assert that the two dates of interest are both present:
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT Name
FROM yourTable
WHERE Date IN ('2001-01-01', '2001-01-31')
GROUP BY Name
HAVING COUNT(DISTINCT Date) = 2
) t2
ON t1.Name = t2.Name
You can uses EXISTS to return a row if there exists another row with that name, having the other A/B grade.
select t1.*
from tablename t1
where t1.Date in ('2001-01-01', '2001-01-31')
and exists (select 1 from tablename t2
where t2.Name = t1.Name
and t2.Date in ('2001-01-01', '2001-01-31')
and t2.Date <> t1.Date)

MS Access: Compare 2 tables with duplicates

I have two tables which look like this:
T1:
ID | Date | Hour
T2:
ID | Date | Hour
I basically need to join these tables when their IDs, dates, and hours match. However, I only want to return the results from table 1 that do not match up with the results in table 2.
I know this seems simple, but where I'm stuck is the fact that there are multiple rows in table 1 that match up with table 2 (there are multiple intervals for any given hour). I need to return all of these intervals so long as they do not fall within the same hour period in table 2.
Example data:
T1:
1 | 1/1/2011 | 1
1 | 1/1/2011 | 1
1 | 1/1/2011 | 1
1 | 1/1/2011 | 2
T2:
1 | 1/1/2011 | 1
1 | 1/1/2011 | 1
My expected result set for this would be the last 2 rows from T1. Can anyone point me on the right track?.
I think you just want not exists:
select t1.*
from t1
where not exists (select 1
from t2
where t2.id = t1.id and t2.date = t1.date and t2.hour = t1.hour
);
EDIT:
I misread the question. This is very hard to do in MS Access. But, you can come close. The following returns the distinct rows in table 1 that do not have equivalent numbers in table 2:
select t1.id, t1.date, t1.hour, (t1.cnt - t2.cnt)
from (select id, date, hour, count(*) as cnt
from t1
group by id, date, hour
) t1 left join
(select id, date, hour, count(*) as cnt
from t2
group by id, date, hour
) t2 left join
on t2.id = t1.id and t2.date = t1.date and t2.hour = t1.hour
where t2.cnt < t1.cnt;