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

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

Related

SQL - How to pick the best available value for each column for each ID from multiple tables?

I have two tables with the same variables referring to attributes of a person.
How can I combine data from two such tables picking the best available value for each column from each table for each field?
Requirements:
For each field, I would like to fill it with a value from either one of the tables, giving a preference to table 1.
Values can be NULL in either table
In the combined table, the value for column 1 could come from table 2 (in case table 1 is missing a value for that person) and the value for column 2 could from table 1 (because both tables had a value, but the value from table 1 is preferred).
In my real example, I have many columns, so an elegant solution with less code duplication would be preferred.
Some users may exist in only one of the tables.
Example:
Table 1:
user_id | age | income
1 | NULL| 58000
2 | 22 | 60000
4 | 19 | 35000
Table 2:
user_id | age | income
1 | 55 | 55000
2 | 19 | NULL
3 | 22 | 33200
Desired output:
user_id | age | income
1 | 55 | 58000
2 | 22 | 60000
3 | 22 | 33200
4 | 19 | 35000
I think that's a full join and priorization logic with colaesce():
select user_id,
coalesce(t1.age, t2.age) as age,
coalesce(t1.income, t2.income) as income
from table1 t1
full join table2 t2 using(user_id)
Use full outer join if user_id in each table is unique.
SELECT
COALESCE(t1.user_id, t2.user_id) AS user_id,
GREATEST(t1.age, t2.age) AS age,
GREATEST(t1.income, t2.income) AS income
FROM t1
FULL OUTER JOIN t2 ON t1.user_id = t2.user_id
try like below using coalesce()
select t1.user_id, coalesce(t1.age,t2.age),
t1.income>t2.income then t1.income else t2.income end as income
table1 t1 join table2 t2 on t1.usesr_id=t2.user_id
You can use below code:
With TableA(Id,age,income) as
( --Select Common Data
select table_1.id,
--Select MAX AGE
case
when table_1.age> table_2.age or table_2.age is null then table_1.age else table_2.age
end,
--Select MAX Income
case
when table_1.income>table_2.income or table_2.income is null then table_1.income else table_2.income
end
from table_1 inner join table_2 on table_2.id=table_1.id
union all
-- Select Specific Data of Table 2
select table_2.id,table_2.age,table_2.income
from table_2
where table_2.id not in (select table_1.id from table_1)
union all
-- Select Specific Data of Table 1
select table_1.id,table_1.age,table_1.income
from table_1
where table_1.id not in (select table_2.id from table_2)
)select * from TableA

Compare columns from 2 different tables with only last inserted values in table_2 in SQL Server

If I have two different tables in a SQL Server 2019 database as follows:
Table1
|id | name |
+-----+--------+
| 1 | rose |
| 2 | peter |
| 3 | ann |
| 4 | rose |
| 5 | ann |
Table2
| name2 |
+--------+
|rose |
|ann |
I would like to retrieve only the last tow ids from table1 (which in this case 4 and 5) that match name2 in table2. In other words, match happens only once on the last added names in table1, furthermore, the ids (4, 5) to be inserted in table2.
How to do that using SQL?
Thank you
You can use row_number()
select name,id from
(
select *, row_number() over(partition by t.name order by id desc) as rn
from table1 t join table2 t1 on t.name=t1.name2
)A where rn=1
Your question is vague, so there could be many answers here. My first thought is that you simply want an inner join. This will fetch ONLY the data that both tables share.
SELECT Table1.*
FROM Table1
INNER JOIN Table2 on Table1.name = Table2.name2
You seem to be describing:
select . . . -- whatever columns you want
from (select top (2) t1.*
from table1 t1
order by t1.id desc
) t1 join
table2 t2
on t2.name2 = t1.name;
This doesn't seem particularly useful for the data you have provided, but it does what you describe.
EDIT:
If you want only the most recent rows that match, use row_number():
select . . . -- whatever columns you want
from (select t1.*,
row_number() over (partition by name order by id desc) as seqnum
from table1 t1
) t1 join
table2 t2
on t2.name2 = t1.name and t1.seqnum = 1;

How can I join two tables with a many-to-one relationship ordered by date?

I need to join two tables and get the most recent record only. Here is the basic form:
table1.id | table1.region | table1.important_col1
1 | NORTH AMERICA | abc
2 | CHINA | def
2 | NORTH AMERICA | hij
table2.id | table2.region | table2.transaction_date | table2.important_col2
1 | NORTH AMERICA | 2/13/2019 | xyz
1 | NORTH AMERICA | 1/13/2019 | zzz
1 | NORTH AMERICA | 12/13/2018 | xxx
desired result:
1 | NORTH AMERICA | 2/13/2019 | abc | xyz
I wanted to use this answer but it seems like I can't use it if I need to group by and then order by descending date. I will need information in multiple columns on the right hand side, but do not want duplicate rows on the left hand side.
The right hand side may have up to 100s of records per id, but I just need something that works for now. Thanks in advance.
edit: I also need to filter the right hand side on other criteria so a simple MAX(table2.transaction_date) won't work.
You can filter your table using internal window function, I used LAG for this example, but you can use ROW_NUMBER and filter several records. Using sliding windows does not change the number of records or counted as SQL aggregation, i.e. you filter using where rather than with having.
SELECT
t1.id
,t2.transaction_date
,t1.region
,t1.col1
,t2.important_col2
FROM table1 AS t1
OUTER APPLY (
SELECT
id
,transaction_date
,LAG(transaction_date,1) over (partition by id order by transaction_date desc) as prev_td
,important_col2
FROM table2
-- WHERE filter_by_col=1 -- additonal "right side" filtering
) as t2
where t1.id = t2.id
and t2.prev_td is null
Output:
1 2019-02-13 00:00:00.000 NORTH AMERICA abc xyz
I used this to test the above query:
create table table1
(id int,
region varchar(30),
col1 varchar(100));
insert into table1
values (1 ,'NORTH AMERICA' ,'abc'),
(2,'CHINA','def'),
(2,'NORTH AMERICA','hij');
create table table2
(id int,
region varchar(30),
transaction_date datetime,
important_col2 varchar(100))
insert into table2
values
(1 ,'NORTH AMERICA',convert(datetime, '02/13/19', 1),'xyz'),
(1 ,'NORTH AMERICA',convert(datetime, '01/13/19',1),'zzz'),
(1 ,'NORTH AMERICA',convert(datetime, '12/13/18',1),'xxx')
Try in this way:
select table11.id, table1.region, max(table2.transaction_date) transaction_date
from table1
inner join table2
on table1.id = table2.id
group by table1.id, table1.region
If there are more columns in table2 (other than transaction date) that you want to display as well, then aggregation alone cannot solve your question.
In MySQL 8.0 you can use window function ROW_NUMBER() to identify the most recent transaction record, as follows :
SELECT x.*
FROM (
SELECT
t1.*,
t2.*,
ROW_NUMBER() OVER(PARTITION BY t2.region ORDER BY t2.transaction_date DESC) rn
FROM table1 t1
INNER JOIN table2 t2 ON t1.region = t2.region
) x
WHERE x.rn = 1
In earlier versions of MySQL, one solution is to add a NOT EXISTS with a correlated subquery that ensures that we are joining with the most recent transaction for the current region :
SELECT t1.*, t2.*
FROM table1 t1
INNER JOIN table2 t2
ON t1.region = t2.region
AND NOT EXISTS (
SELECT 1
FROM table2
WHERE region = t2.region AND transaction_date > t2.transaction_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;

Joining two sql tables with a one to many relationship, but want the max of the second table

I am trying to join two tables one is a unique feature the seconds is readings taken on several dates that relate to the unique features. I want all of the records in the first table plus the most recent reading. I was able to get the results I was looking for before adding the shape field. By using the code
SELECT
Table1.Name, Table1.ID, Table1.Shape,
Max(Table2.DATE) as Date
FROM
Table1
LEFT OUTER JOIN
Table2 ON Table1.ID = table2.ID
GROUP BY
Table1.Name, Table1.ID, Table1.Shape
The shape field is a geometry type and I get the error
'The type "Geometry" is not comparable. It can not be use in the Group By Clause'
So I need to go about it a different way, but not sure how.
Below is a sample of the two tables and the desired results.
Table1
Name| ID |Shape
AA1 | 1 | X
BA2 | 2 | Y
CA1 | 3 | Z
CA2 | 4 | Q
Table2
ID | Date
1 | 5/27/2013
1 | 6/27/2014
2 | 5/27/2013
2 | 6/27/2014
3 | 5/27/2013
3 | 6/27/2014
My Desired Result is
Name| ID |Shape |Date
AA1 | 1 | X | 6/27/2014
BA2 | 2 | Y | 6/27/2014
CA1 | 3 | Z | 6/27/2014
CA2 | 4 | Q | Null
You can do the aggregation on Table2 in a CTE, finding the MAX(DATE) for each ID, and then join that result to Table1:
WITH AggregatedTable2(ID, MaxDate) AS
(
SELECT
ID, MAX(DATE)
FROM
Table2
GROUP BY
ID
)
SELECT
t1.ID, t1.Name, t1.Shape, t2.MaxDate
FROM
Table1 t1
LEFT JOIN
AggregatedTable2 t2 ON t1.ID = t2.ID
Try casting geometry as a varchar.
Select Table1.Name, Table1.ID, cast(Table1.Shape as varchar(1)) AS Shape, Max(Table2.DATE) as Date
FROM Table1 LEFT OUTER JOIN
Table2 ON Table1.ID = table2.ID
Group By Table1.Name, Table1.ID, cast(Table1.Shape as varchar(1))
Try this:
SELECT t1.Name
, t1.ID
, t1.Shape
, MAX(t2.Date) As Date
FROM Table1 AS t1
LEFT JOIN Table2 AS t2
ON t2.ID = t1.ID
GROUP
BY t1.Name
, t1.ID
, t1.Shape