Instead of doing something like this:
SELECT
t1.*,
(SELECT TOP 1 t2.Name FROM Table2 t2 Order By t2.Number) Val1
FROM Table1 t1
How would it be done with a Join instead?
SELECT
t1.*,
TOP 1 t2.Name
FROM Table1 t1
JOIN Table2 t2 Order By t2.Number
or is it even possible?
select top 1 * from (SELECT t1.*, t2.Number num FROM table1 t1 JOIN table2 t2 on t1.commoncolumn=t2.commoncolumn ) t order by t.num
Try this (id is the join field)
with Table2Data as
(
SELECT Id , Name ,row_number() over(partition by Id order by number) rowNumber FROM Table2
)
select t1.* ,t2.name from table1 t1
inner join Table2Data t2 on t1.Id=t2.Id and t2.rowNumber=1
The following accomplishes the same thing.
SELECT t1.*, MIN(t2.Name) OVER() val1 FROM Table1 INNER JOIN Table2 t2 on t1.id = t2.id
It can be done with a CROSS APPLY. Assuming Id is the common field:
SELECT t1.*
, t3.Name
FROM Table1 t1
CROSS APPLY (
SELECT TOP 1 t2.Name
FROM Table2 t2
WHERE t1.Id = t2.Id
ORDER BY t2.Number
) t3
Related
I have two tables that I am trying to JOIN
table1
----------------------------
Id Name Num
123X Apple 17
table2
-------------------------------------------------
id EndDt SomeVal
123X 10/1/2021 xxx
123X 3/1/2022 yyy
I am attempting to Select from table1 a and LEFT JOIN table2 b on a.id = b.id - however, I want to only select on the id in table2 where MAX(EndDt)
Select a.*, b.SomeVal
from table1 a
LEFT OUTER JOIN table2 b on a.id=b.id // and b.MAX(EndDt)
Is something like that doable?
There are a few ways you can do this. I make some assumptions on your data though.
Use a LEFT JOIN with a subquery:
SELECT T1.*,
sq.SomeVal
FROM dbo.Table1 T1
LEFT JOIN (SELECT ROW_NUMBER() OVER (PARTITION BY t2.Id ORDER BY t2.EndDt DESC) AS RN,
t2.Id,
t2.SomeVal
FROM dbo.Table2 T2) sq ON T1.Id = T2.Id
AND T2.RN = 1;
Use APPLY and TOP:
SELECT T1.*,
sq.SomeVal
FROM dbo.Table1 T1
OUTER APPLY (SELECT TOP (1)
t2.Id,
t2.SomeVal
FROM dbo.Table2 T2
WHERE T2.Id = T1.Id
ORDER BY T2.EndDt DESC) sq;
Use a CTE and get the "top 1" row per group:
WITH CTE AS(
SELECT T1.*,
T2.SomeVal,
ROW_NUMBER() OVER (PARTITION BY T1.ID ORDER BY T2.MaxDt DESC) AS RN
FROM dbo.Table1 T1
LEFT JOIN dbo.Table2 T2 ON T1.Id = T2.Id)
SELECT *
FROM CT
WHERE RN = 1;
Use TOP (1) WITH TIES:
SELECT TOP (1) WITH TIES
T1.*,
T2.SomeVal
FROM dbo.Table1 T1
LEFT JOIN dbo.Table2 T2 ON T1.Id = T2.Id
ORDER BY ROW_NUMBER() OVER (PARTITION BY T1.ID ORDER BY T2.MaxDt DESC) ASC;
Note that options 3 and 4 won't work as expected if ID is not unique in the table Table1 (hence my assumptions about your data).
I would recommend using the windowed ROW_NUMBER function to take the max table2 first, and then join into that subquery.
;WITH cte AS (
SELECT *, [Row] = ROW_NUMBER() OVER (PARTITION BY b.Id ORDER BY b.EndDt DESC)
FROM table2 b
)
SELECT a.*, cte.SomeVal
FROM table1 a
LEFT JOIN cte ON a.id = cte.id AND cte.[Row] = 1
For a single value, use a correlated sub-query:
SELECT
a.id,
a.name,
a.num,
(
SELECT TOP 1 SomeValue
FROM table2 As b
WHERE b.id = a.id
ORDER BY b.EndDt DESC
) As SomeVal
FROM
table1 a
Is there a way to display only records where one email corresponds to more than 3 names?
I have tried the code below, but it does not return anything.
SELECT
t1.Name, t2.Email
FROM
Table1 t1
JOIN Table2 t2 on t1.ID=t2.PersonID
GROUP BY
t1.Name, t2.Email
HAVING COUNT(t2.Email) > 3
If your version supports window function then you can do :
select t.*
from (select t1.name, t2.email,
count(t2.email) over (partition by t1.name) as cnt
from t1 inner join
t2
on t2.personid = t1.id
) t
where cnt > 3;
I think you want:
SELECT t1.Name, GROUP_CONCAT(t2.Email)
FROM Table1 t1 JOIN
Table2 t2
ON t1.ID = t2.PersonID
GROUP BY t1.Name
HAVING COUNT(t2.Email) > 3;
The big change is to the GROUP BY -- this does not have EMAIL. You seem to want the emails returns on each row, so this concatenates them together.
EDIT:
In SQL Server, you would use string_agg():
SELECT t1.Name, STRING_AGG(t2.Email, ',')
FROM Table1 t1 JOIN
Table2 t2
ON t1.ID = t2.PersonID
GROUP BY t1.Name
HAVING COUNT(t2.Email) > 3;
Or, if you want individual rows:
SELECT n.*
FROM (SELECT t1.Name, t2.Email,
COUNT(*) OVER (PARTITION BY t1.Name) as cnt
FROM Table1 t1 JOIN
Table2 t2
ON t1.ID = t2.PersonID
) n
WHERE cnt > 3
ORDER BY Name;
Is it possible to access fields across derived tables?
SELECT *
FROM (SELECT ID, COL1A FROM Table1) T1
JOIN (SELECT ID, COL2A FROM Table2) T2
ON T1.ID = T2.ID
JOIN (SELECT ID, (COL3A + T2.COL2A) AS SUM FROM Table3) T3
ON T1.ID = T3.ID
You would put the expressions using multiple columns in the SELECT clause:
SELECT t1.ID, t1.COL1A, t2.COL2A, (t3.COL3A + t2.COL2A) as sum
FROM Table1 T1 JOIN
Table2 T2
ON T1.ID = T2.ID JOIN
Table3 T3
ON T1.ID = T3.ID;
There is no need for derived tables at all.
i have this tables:
Table1:
id Name
1 Example1
2 Example2
Table2:
id Date..............
1 5.2.2014........
1 6.2.2014.........
1 6.2.2014........
2 16.1.2014.......
2 17.1.2014.......
And I need take id and Name from table1 and join table1.id = table2.id and from table2 take only top 1 row...
Example:
id Name Date
1 Example1 5.2.2014
2 Example2 16.1.2014
It is possible?
You can use row_number() to filter out all but the latest row per id:
select *
from (
select row_number() over (partition by id order by Date desc) as rn
, *
from Table2
) as t2
join Table1 as t1
on t1.id = t2.id
where t2.rn = 1 -- Only latest row
Well, a simple attempt would be
SELECT t1.*,
(SELECT TOP 1 t2.Date FROM Table2 t2 WHERE t2.ID = t1.ID t2.Date) t2Date
FROM Table1 t1
If you were using SQL Server, you could use ROW_NUMBER
Something like
;WITH Vals AS (
SELECT t1.ID,
t1.Name,
t2.Date,
ROW_NUMBER() OVER(PARTITION BY t1.ID ORDER BY t2.Date) RowID
FROm Table1 t1 LEFT JOIN
Table2 t2 ON t1.ID
)
SELECT *
FROM Vals
WHERE RowID = 1
Select t1.id, t1.name , MIN(t2.date)
From table1 t1
Inner Join table2 t2
On t1.id=t2.id
Group By t1.id, t1.name
how can I rewrite the following query using JOIN
SELECT *
FROM table1
WHERE id NOT IN
(
SELECT t1Id
FROM table2
);
SELECT *
FROM table1 t1
left outer join table2 t2 on t1.id=t2.id
where t2.id is null
SELECT * FROM table1 t1
WHERE NOT EXISTS(
SELECT *
FROM table2 t2
Where t1.Id = t2.t1Id);