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)
Related
Currently trying to figure out how to implement a SQL LEFT OUTER JOIN while using the SQL WITH AS clause. My code breaks down into 3 SELECT statements while using the same table, then using LEFT OUTER JOIN to merge another table on the id.
I need 3 SELECT statements before joining because I need a SELECT statement to grab the needed columns, ROW RANK the time, and set WHERE clause for the ROW RANK.
SELECT *
(
WITH employee AS
(
SELECT id, name, department, code, time, reporttime, scheduled_time
FROM table1 AS a
WHERE department = "END"
),
employe_v2 as
(
SELECT address
,ROW_NUMBER() OVER (PARTITION BY id ORDER BY time desc, reporttime desc, scheduled_time desc) AS row_rank
FROM table1 AS b
)
SELECT *
FROM employee, employee_v2
WHERE row_rank = 1
) t1
LEFT OUTER JOIN
(
SELECT b.id, b.new_code, b.date
FROM table2 AS b
WHERE b.newcode != "A"
) t2
ON t1.id = t2.id
Group BY t1.id, t1.name, t1.department, t1.code, t1.time, t1.reporttime,
t1.scheduled_time, t1.row_rank, t2.id, t2.new_code, t2.date
How I could fix my code?
not sure if group by is needed, i see no aggregation whatsover
but if it's something you need , you can add at the end of final select and ofcourse you have to take care of columns/aggregation in select
nevertheless you can simplify your query as below :
with employee as (
select * from (
select id, name, department, code, time, reporttime, scheduled_time, address
,row_number() over (partition by id order by time desc, reporttime desc, scheduled_time desc) AS row_rank
from table1
) t where row_rank =1
)
select t1.*, b.id, b.new_code, b.date
from employee t1
left join table2 as t2
on t1.id = t2.id
where t2.newcode != "A"
To get the number of rows from a table, I can use SELECT COUNT( row-name ) for the joined table.
But this doesn't work if I filter it using QUALIFY ROW_NUMBER() OVER ( PARTITION BY rowx, rowy) = 1
Is there a way to get the total number of rows for a QUALIFY filtered table?
Here is a full example of the query
query = """
SELECT
COUNT(*)
FROM table1
JOIN table2 ON
table1.column1 = table2.column2
JOIN table2 ON
table1.column4 = table3.column5
QUALIFY ROW_NUMBER() OVER
(
PARTITION BY
table3.column6,
table3.column7
) = 1
"""
I also tried
query = """
SELECT
COUNT(*)
FROM (
table1
JOIN table2 ON
table1.column1 = table2.column2
JOIN table2 ON
table1.column4 = table3.column5
QUALIFY ROW_NUMBER() OVER
(
PARTITION BY
table3.column6,
table3.column7
) = 1
)
"""
But it didn't work
Most likely QUALIFY is happening after the COUNT(*) expression is being evaluated. To remedy this, you may take the count of a subquery:
SELECT COUNT(*)
FROM
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY t3.column6, t3.column7) rn
FROM table1 t1
INNER JOIN table2 t2 ON t1.column1 = t2.column2
INNER JOIN table3 t3 ON t1.column4 = t3.column5
) t
WHERE rn = 1;
I have about 10 tables that I make one big nested tables by rounds with the following query:
R1 AS(
SELECT ANY_VALUE(Table1).*, ARRAY_AGG(( SELECT AS STRUCT Table2.* EXCEPT(ID))) AS Table2
FROM Table1 LEFT JOIN Table2 USING(ID)
GROUP BY Table1.ID),
R2 AS(
SELECT ANY_VALUE(R1).*, ARRAY_AGG(( SELECT AS STRUCT Table3.* EXCEPT(ID))) AS Table3
FROM R1 LEFT JOIN Table3 USING(ID)
GROUP BY R1.ID),
...
SELECT ANY_VALUE(R9).*, ARRAY_AGG(( SELECT AS STRUCT Table10.* EXCEPT(ID))) AS Table10
FROM R9 LEFT JOIN Table10 USING(ID)
The thing is that for example in my first table I can have two records with the same ID but some other fields will be different and I want to consider them as two distinct records and thus group by all the fields of the table while I join.
Then I want to do the same with all the "sub-table" (the R tables in the query), so I will able to group by all the fields of the nested tables.
How can I do it easily ?
I tried GROUP BY Table1.* but it doesn't work...
Thank you in advance
Try to_json_string:
...
FROM Table1 t1
...
GROUP BY to_json_string(t1)
You seem to want something like this:
select *
from table1 t1 left join
(select t2.*
from table2 t2
where true
qualify row_number() over (partition by t2.id order by t2.id) = 0
) t2
using (id)
This uses qualify instead of group by to fetch one row.
If you don't want all rows from from table1, you can whittle them down as well:
select *
from (select t1.*
from table1 t1
where true
qualify row_number() over (partition by id, col1, col2 order by id) = 1
) t1 left join
(select t2.*
from table2 t2
where true
qualify row_number() over (partition by t2.id order by t2.id) = 0
) t2
using (id)
How to Group By all fields ...?
I tried GROUP BY Table1.* but it doesn't work...
Consider below example
SELECT ANY_VALUE(t1).*,
ARRAY_AGG(( SELECT AS STRUCT t2.* EXCEPT(ID))) AS Table2
FROM Table1 t1 LEFT JOIN Table2 t2 USING(ID)
GROUP BY FORMAT('%t', t1)
Edit: I’m using teradata sql assistant
I have two tables TABLE_1 and TABLE_2
TABLE_1:
TABLE_2:
And I want to left join TABLE_1 and TABLE_2 so that I am always taking the record that is of the most recent date from TABLE_2. The end result should look like:
How do I specify my join condition?
SELECT DISTINCT
TABLE_1.COLUMN_NUM,
TABLE_2.COLUMN_2
FROM TABLE_1
LEFT JOIN TABLE_2
ON TABLE_1.COLUMN_NUM=TABLE_2.COLUMN_1
AND TABLE_2.DATE = (MAXIMUM DATE FROM TABLE_2)
The query you wanted to write seems to be:
select t1.column_num, t2.column_2
from table_1 t1
left join table_2 t2
on t2.column_1 = t1.column_num
and t2.date = (
select max(t22.date) from table_2 t22 where t22.column_1 = t1.column_1
)
The downside, though, is that it requires two scans on table_2. For this dataset and expected resultset, I would recommend a correlated subquery with a row-limiting clause:
select
t1.column_num,
(
select column2
from table_2 t2
where t2.column_1 = t1.column_num
order by t2.date desc
limit 1
) column_2
from table_1 t1
This is rather cross-database approach, although the syntax for the row-limiting clause may vary: the above would work in MySQL and Postgres, while SQL Server wants select top (1) ... from ... where ... order by ....
This query would take advantage of an index on table_2(column_1, date, column_2) (thr ordering of the columns in the index matters here).
This query would take advantage of an index on table_2(column_1, date, column_2) (thr ordering of the columns in the index matters here).
If your database supports window functions, you can also join and use row_number():
select t1.column_num, t2.column_2
from table_1 t1
left join (
select t2.*, row_number() over(partition by column_2 order by date desc) rn
from table_2 t2
) t2 on t2.column_1 = t1.column_num and t2.rn = 1
I am not sure which database you use, but for postgresql
SELECT DISTINCT ON ( TABLE_1.COLUMN_NUM)TABLE_1.COLUMN_NUM, TABLE_2.COLUMN_2 FROM TABLE_1 LEFT JOIN TABLE_2 ON TABLE_1.COLUMN_NUM=TABLE_2.COLUMN_1 ORDER BY TABLE_2.DATE DESC
You can try the below statement:
With temp AS(Select column_num, column_2, date, max(b.date) OVER (PARTITION BY b.column_1) max_dt
from table_1 a LEFT JOIN table_2 b ON a.column_num = b.column_1)
Select column_num, column_2 from temp
where date = max_dt;
This is the same as GMB's 3rd query, just filtering before the join using Teradata's proprietary QUALIFY clause:
select t1.column_num, t2.column_2
from table_1 t1
left join (
select t2.*
from table_2 t2
qualify row_number()
over(partition by column_2
order by date desc) = 1
) t2
on t2.column_1 = t1.column_num
I am trying to select multiple values from two tables but i want to group by single value. I have tried using max(value) in select but max is returning the greatest one and not the exact one.
Here are my tables
The result i need is something like this
Result : HeadQuarterId - A, PropertyName - Name1, Amount - 102
HeadQuarterId - B, PropertyName - Name5, Amount - 30
Here is my query
SELECT Headquarterid,Max(PropertyName),sum(Amount)
FROM Table1 A LEFT OUTER JOIN Table2 B
ON A.Propetyid = B.PropertyId
GROUP BY Headquarterid
Here i have used Left Outer Join so that i will get all the data from left table even it is not available in right table.
Also i cannot use A.HeadquarterID = A.PropertyId in where condition since i have other dependency in that table. Please suggest someother way to achieve this result.
I think I understand. You want the headquarters with the maximum value, which happens to be A. If so:
select t1.*, sum(t2.amount) over () as total
from t1 left join
t2
on t2.PropertyId = t1.PropertyId
order by t2.amount desc
fetch first 1 row only;
Note: Not all databases support fetch first. It might be spelled limit or use select top (1) for instance.
I would recommend to get the headquartename per ID in a cte / subquery, then join it again to T1 and left join T1 to T2 in a second cte / subquery. This way you can calculate your sums basing on a single group:
WITH cte AS(
SELECT ROW_NUMBER() OVER (PARTITION BY t1.ID ORDER BY CASE WHEN t1.ID = t1.PROPERTYID THEN 0 ELSE 1 END) rn, t1.ID, t1.Name
FROM t1
),
cte2 AS(
SELECT c.name cName, t1.*, t2.Value
FROM t1
INNER JOIN cte c ON c.ID = t1.ID AND c.rn = 1
LEFT JOIN t2 ON t1.Propertyid = t2.propertyid
)
SELECT c2.id, c2.cname, sum(c2.value) value
FROM cte2 c2
GROUP BY c2.id, c2.cname
See SQLFiddle for details: http://sqlfiddle.com/#!18/8bf66/13/2
Of course you can build the first cte without the row_number only by using the WHERE ID = PROPERTYID - matter of taste I'd say...
As per your sample data you want window function :
select distinct t1.HeadQuarterId,
max(t1.PropertyName) over (partition by t1.HeadQuarterId) as PropertyName,
sum(t2.amount) over (partition by t1.HeadQuarterId) as amount
from t1 left join
t2
on t2.PropertyId = t1.PropertyId;
This provided the result i expected.
SELECT HQTRS1 AS headId,Max(LLORD1) AS headName, sum(Amount) AS amount
FROM
(SELECT DISTINCT HeadQuarterId AS HQTRS1, PropertyName AS LLORD1 FROM Table_1 WHERE HeadQuarterId = PropertyId) AS temp
INNER JOIN Table_1 AS A ON A.HeadQuarterId = temp.HQTRS1
LEFT OUTER JOIN Table_2 B
ON B.PropertyId = A.PropertyId
GROUP BY HQTRS1