SQL Join to the latest record - sql

I want to join tables in such a way that it fetches only latest record from one of the tables:
The following are my data
Table_One:
+----+------+
| ID | Name |
+----+------+
| 1 | John |
| 2 | Tom |
| 3 | Anna |
+----+------+
Table_two:
+----+----------+-----------+
| ID | Visit ID | Date |
+----+----------+-----------+
| 1 | 2513 | 5/5/2001 |
| 1 | 84654 | 10/5/2012 |
| 1 | 454 | 4/20/2018 |
| 2 | 754 | 4/5/1999 |
| 2 | 654 | 8/8/2010 |
| 2 | 624 | 4/9/1982 |
| 3 | 7546 | 7/3/1997 |
| 3 | 246574 | 6/4/2015 |
| 3 | 15487 | 3/4/2017 |
+----+----------+-----------+
Results needed after Join:
+----+------+----------+-----------+
| ID | Name | Visit ID | Date |
+----+------+----------+-----------+
| 1 | John | 454 | 4/20/2018 |
| 2 | Tom | 654 | 8/8/2010 |
| 3 | Anna | 246574 | 6/4/2015 |
+----+------+----------+-----------+

Different database engines have varying ways to get the top row from table 2 per group (you can google for "SQL windowing functions" and your product). Since you don't state what engine you're using it's impossible to give the most appropriate or most performant solution.
The following method should work in most or all SQL engines but will not be especially performant over a large data set (it will benefit from a composite index Table2(ID, Date)). The details of how you specify the aliases for the tables may differ a bit among engines but you can use this as a guide. A windowing function solution will probably be more efficient.
SELECT ID, Name, VisitID, Date FROM Table1 T1 INNER JOIN Table2 T2 +
ON T1.ID = T2.ID
WHERE NOT EXISTS (SELECT * FROM Table2 T2B WHERE T2B.ID = T1.ID AND T2B.Date > T2.Date)

I suspect you have SQL Server if so, then you can use APPLY:
select o.*, tt.*
from Table_One o
cross apply ( select top 1 t.VisitDate, t.Date
from table_two t
where t.id = o.id
order by t.date desc
) tt;

You can filter out "latest visit" using
SELECT ID,MAX(DATE) FROM TABLE_TWO GROUP BY ID;
You then join that to TABLE_ONE (... ON .ID = .ID) to pick up the Name column and then join that again to TABLE_TWO (... ON ID=ID AND DATE=DATE) if you need to pick up the VISIT_ID as well.
Specific DBMS's might have proprietary/idiosyncratic extensions typically serving the purpose of allowing the optimizer do a better job (e.g. allowing the optimizer to understand that the "joining back to TABLE_TWO can be eliminated). Thinking here of SELECT TOP 1 ... and the like.

SELECT ID,Name,Visit_ID,Date
FROM
(SELECT *, ROW_NUMBER() OVER(PARTITION BY ID Date DESC) as seq
FROM Table2 LEFT OUTER JOIN
Table1 ON Table2.ID = Table1.ID) as mainTable
WHERE seq = 1

I'm not a 100% sure if this is correct since the Visit ID might just throw every record right back at you. However you can find some great documentation here: https://www.w3resource.com/sql/aggregate-functions/max-date.php
select t1.ID,t1.Name,t2.visit_ID, Max(t2.Date) from Table_Two t2
inner join Table_One t1
on(t2.ID=t1.ID)
group by t1.ID,t1.Name,t2.visit_ID
something like this should work though, i think that this is also the same as #Erwin Smout proposes
select a.ID, t1.Name, a.date,t2.Visit_ID (
select ID, max(date)'date' from Table_Two
group by ID) a
inner join Table_One t1
on( a.ID=t1.ID)
inner join Table_Two t2
on(a.ID=t2.ID and a.Date=t2.Date)

This is the answer to your question
SELECT t1.ID, t1.Name, t2.visit_id, t2.Date FROM table1 t1 INNER JOIN table2 t2 ON t1.ID = t2.ID WHERE NOT EXISTS (SELECT * FROM table2 t2b WHERE t2b.ID = t1.ID AND t2b.Date > t2.Date)

Related

Get Last Row or Smallest data then join two tables

Using this Question where it is related Get Last Row of Different id then display data that is greater than zero
I would like to join another table
This is my table2
+----+--------+
| id | name |
+----+--------+
| 1 | Taylor |
| 2 | Zac |
| 3 | Scott |
| 4 | Emma |
+----+--------+
If I use this code...
SELECT DISTINCT t1.id, t1.amount, t2.name FROM table1 t1, table2 t2 WHERE t1.amount = (SELECT MIN(t1.amount) FROM table1 WHERE id= t1.id) AND t1.[t1.amount]>0 AND t1.[id]=t2.[id];
... then these result show...
+----+--------+-------+
| id | amount | name |
+----+--------+-------+
| 2 | 100 | Zac |
+----+--------+-------+
only 1 data is showing
what i want is both data in the table
+----+--------+-------+
| id | amount | name |
+----+--------+-------+
| 2 | 100 | Zac |
| 4 | 200 | Emma |
+----+--------+-------+
SELECT DISTINCT t.id,t2.name,t.amount
FROM table t
INNER JOIN table2 t2 ON t.id=t2.id
WHERE amount = (SELECT MIN(amount) FROM table WHERE id= t.id)
and amount>0;
Believe you're looking for a subquery of the first table, try the following:
SELECT DISTINCT t1.id, t1.amount, t2.name
FROM (SELECT DISTINCT id, amount FROM table t
WHERE amount = (SELECT MIN(amount) FROM table WHERE id= t.id)
and amount>0) t1, table2 t2
WHERE t1.[id]=t2.[id];
Not sure why your current query isn't working but hope this helps!
Please try the following...
SELECT table2.id AS id,
minAmount AS amount,
name
FROM ( SELECT id,
MIN( amount ) AS minAmount
FROM table1
GROUP BY id
HAVING MIN( amount ) > 0
) AS table1Refiner
INNER JOIN table2 ON table1Refiner.id = table2.id;
This statement works by obtaining the results as per your first Question and joining them to table2 based on their shared values of id, effectively appending the corresponding name to each id and amount. The fields are then selected and output.
If you have any questions or comments, then please feel free to post a Comment accordingly.

SQL query to get all the rows from multiple tables even it has uneven primary and foreign keys

I have multiple tables, like below
Table-1:
ID | Name
----------------
1 | John
2 | Doe
3 | Mark
4 | Hill
5 | Ram
Table-2:
ID | place
----------------
1 | CA
2 | NY
4 | AK
5 | CT
Table-3:
ID | age
----------------
2 | 35
3 | 23
4 | 54
I need to get the output table as
Table-1:
ID | Name | Place | Age
-----------------------------------
1 | John | CA |
2 | Doe | NY | 35
3 | Mark | | 23
4 | Hill | AK | 54
5 | Ram | CT |
I tried using join and union, however I could not find the solution. Could you please someone give me the solutions?
You need to use join as below:
select t1.id, t1.name,t2.place, t3.age
from table1 t1 join table2 t2 on t1.id = t2.id
left join table3 t3 on t1.id = t3.id
The result as shown can be obtained using an outer join:
select *
from table1
left join table2 using (id)
left join table3 using (id);
The join through the using operator has the nice side effect that the ID column will only show up once in the result even though I have used select *
Your example doesn't show this, but if you have IDs in table2 that are not in table1 but you still want them in the result, you need to use a full outer join.
select *
from table1
full join table2 using (id)
full join table3 using (id);
The using (id) know has another nice side effect: it will automatically use the ID value from whichever table the id came from without the need to use a coalesce(id) in the select list.
select t1.id, name, place, age
from table1 t1
` left outer join table2 on t1.id=t2.id
left outer join table3 on t1.id=t3.id;

SQL Query to Bring Back where Row Count is Greater than 1

I have two tables.They have the same data but from different sources. I would like to find all columns from both tables that where id in table 2 occurs more than once in table 1. Another way to look at it is if table2.id occurs only once in table1.id dont bring it back.
I have been thinking it would be some combination of group by and order by clause that can get this done but its not getting the right results. How would you express this in a SQL query?
Table1
| id | info | state | date |
| 1 | 123 | TX | 12-DEC-09 |
| 1 | 123 | NM | 12-DEC-09 |
| 2 | 789 | NY | 14-DEC-09 |
Table2
| id | info | state | date |
| 1 | 789 | TX | 14-DEC-09 |
| 2 | 789 | NY | 14-DEC-09 |
Output
|table2.id| table2.info | table2.state| table2.date|table1.id|table1.info|table1.state|table1.date|
| 1 | 789 | TX | 14-DEC-09 | 1 | 123 | TX | 12-DEC-09 |
| 1 | 789 | TX | 14-DEC-09 || 1 | 123 | NM | 12-DEC-09 |
If you using MSSQL try using a Common Table Expression
WITH cte AS (SELECT T1.ID, COUNT(*) as Num FROM Table1 T1
INNER JOIN Table2 T2 ON T1.ID = T2.ID
GROUP BY T1.ID
HAVING COUNT(*) > 1)
SELECT * FROM cte
INNER JOIN Table1 T1 ON cte.ID = T1.ID
INNER JOIN Table2 T2 ON cte.ID = T2.ID
First, I would suggest adding an auto-incrementing column to your tables to make queries like this much easier to write (you still keep your ID as you have it now for relational-mapping). For example:
Table 1:
TableID int
ID int
Info int
State varchar
Date date
Table 2:
TableID int
ID int
Info int
State varchar
Date date
Then your query would be really easy, no need to group, use CTEs, or row_over partitioning:
SELECT *
FROM Table2 T2
JOIN Table1 T1
ON T2.ID = T1.ID
JOIN Table1 T1Duplicate
ON T2.ID = ID
AND T1.TableID <> T1Duplicate.TableID
It's a lot easier to read. Furthermore, there are lots of scenarios where an auto-incrementing ID field is benefitial.
I find this a much simpler way to do it:
select TableA.*,TableB.*
from TableA
inner join TableB
on TableA.id=TableB.id
where TableA.id in
(select distinct id
from TableA
group by id
having count(*) > 1)

Can't update main table with joined tables using DISTINCT - Access: This recordset is not updatable

gurus!
I'm using SQL Server linked tables in Access Forms. In MainTable I need to update and insert records, but Access won't let it, for update it says "This Recordset is not updateable". I know, it's couse DISTINCT, but it's necessary for TableType records - I need only one related name_ds from TableTypes (even first by npr) and in result just thees 7 MainTable records not 16 (without DISTINCT).
Any workarounds?
Simple structure -
MainTable: id, npr, name, type, datasource_fk.
TableDS: id, name_ds, something.
TableType: id, npr, name_type, something_type.
Data -
MainTable:
1;12;"Olie";"percentage";1
2;15;"Tol";"count";2
3;13;"Opp";"percentage";1
4;12;"Hypq";"count";3
5;14;"Gete";"count";1
6;;"Mour";"count";2
7;;"Ellt";"percentage";3
TableDS:
1;"City1";"q"
2;"City2";"a"
3;"State1";"z"
4;"State2";"x"
TableType:
1;12;"City1";"w"
2;15;"City1";"s"
3;13;"City1";"x"
4;14;"City2";"w"
5;14;"City1";"s"
6;13;"City3";"p"
7;12;"City1";"t"
8;12;"City1";"n"
9;12;"State1";"r"
10;15;"State1";"r"
SQL, result -
SELECT DISTINCT t3.npr AS npr_type, t1.npr, t1.id, t1.name, t2.name_ds, t1.datasource_fk, t1.types
FROM (MainTable AS t1 LEFT JOIN TableDS AS t2 ON t1.datasource_fk = t2.id) LEFT JOIN TableType AS t3 ON t1.npr = t3.npr;
---------------------------------------------------------------------------------------------------------------------------------------------
| npr_type | npr | id | name | name_ds | datasource_fk | types |
---------------------------------------------------------------------------------------------------------------------------------------------
| | | 6 | Mour | City2 | 2 | count |
---------------------------------------------------------------------------------------------------------------------------------------------
| | | 7 | Ellt | State1 | 3 | percentage |
---------------------------------------------------------------------------------------------------------------------------------------------
| 12 | 12 | 1 | Olie | City1 | 1 | percentage |
---------------------------------------------------------------------------------------------------------------------------------------------
| 12 | 12 | 4 | Hypq | State1 | 3 | count |
---------------------------------------------------------------------------------------------------------------------------------------------
| 13 | 13 | 3 | Opp | City1 | 1 | percentage |
---------------------------------------------------------------------------------------------------------------------------------------------
| 14 | 14 | 5 | Gete | City1 | 1 | count |
---------------------------------------------------------------------------------------------------------------------------------------------
| 15 | 15 | 2 | Tol | City2 | 2 | count |
---------------------------------------------------------------------------------------------------------------------------------------------
You are getting 16 matches on your joins because MainTable npr column matches multiple times with TableType npr column.
1;12;"Olie";"percentage";1
Matches to
7;12;"City1";"t"
8;12;"City1";"n"
9;12;"State1";"r"
1;12;"City1";"w"
Your best bet is to use a where clause for column TableType.somethingtype. You can try LEFT JOIN on TableDS and TableType using multiple columns but really, you may need to adjust your data. In other words, inactivate some rows. The following query will show you what you're up against:
SELECT t3.npr AS npr_type,
t1.npr,
t1.id,
t1.name,
t2.name_ds,
t1.datasource_fk,
t1.type,
t3.something_type
FROM #MainTable t1
LEFT JOIN #TableDS AS t2
ON t1.datasource_fk = t2.id
LEFT JOIN #TableType AS t3
ON t1.npr = t3.npr
ORDER BY t3.npr,
t1.npr,
t1.id,
t1.name,
t2.name_ds,
t1.datasource_fk,
t1.type,
t3.something_type
So, after you figure out your data. Then you may be able to do something like:
SELECT t3.npr AS npr_type,
t1.npr,
t1.id,
t1.name,
t2.name_ds,
t1.datasource_fk,
t1.type,
t3.something_type
FROM #MainTable t1
LEFT JOIN #TableDS AS t2
ON t1.datasource_fk = t2.id
LEFT JOIN #TableType AS t3
ON t1.npr = t3.npr
WHERE
(t1.npr = 12 AND t3.something_type = 'n')
OR
(t1.npr = 14 AND t3.something_type = 's')
OR
(t1.npr = 13 AND t3.something_type = 'p')
OR
(t1.npr = 15 AND t3.something_type = 's')
OR
(t1.npr IS NULL)

SQL Plus Query - Multiple Table Query

This is causing me more trouble then it should.
I have the following sample tables:
____________________ ____________
| Name| Number | | Number |
|_______|__________| |__________|
| Alice | 1 | | 1 |
| Bob | 2 | | 1 |
|_______|__________| |__________|
I want my result to be:
_____________________________________
| Name | Number | Count(Number) |
|________|___________|_______________|
| Alice | 1 | 2 |
| Bob | 2 | 0 |
|________|___________|_______________|
I'm going back and forward but I'm sure this shouldn't be so tricky. I assume I'm missing something.
I've modified Gordon's answer:
select name, number count(t2.number)
from table1 t1,
table2 t2
where t1.number = t2.number (+)
group by t1.number;
You need a join and aggregation. However, the join needs to be a left outer join to keep all the rows:
select name, number, count(t2.number)
from table1 t1 left outer join
table2 t2
on t1.number = t2.number
group by t1.name, t1.number;
And, the count() is counting the non-NULL values in the second table, so you can get 0 when there is no match.