I have data like the below, and would like to see results like below.
The blank spaces should be present in the results. Either Table 2 or Table 3 may contain more rows than the other, and blanks/or/nulls should be in the result.
Table 1
C1=29 (pk) C2=4133
C1=33 (pk) C2=9375
Table 2
C1=29(fk) C2=Adam
C1=29(fk) C2=Bob
C1=29(fk) C2=Chris
C1=29(fk) C2=Dave
C1=33(fk) C2=Eddie
C1=33(fk) C2=Frank
Table 3
C1=29(fk) C2=Helen
C1=29(fk) C2=Joice
C1=33(fk) C2=Karen
C1=33(fk) C2=Laura
C1=33(fk) C2=Mary
Desired result
1.C1=29 1.C2=4133 2.C2=Adam 3.C2=Helen
1.C1=29 1.C2=4133 2.C2=Bob 3.C2=Joice
1.C1=29 1.C2=4133 2.C2=Chris
1.C1=29 1.C2=4133 2.C2=Dave
1.C1=33 1.C2=9375 2.C2=Eddie 3.C2=Karen
1.C1=33 1.C2=9375 2.C2=Frank 3.C2=Laura
1.C1=33 1.C2=9375 3.C2=Mary
Rohit-s query gives --
SELECT t1.col1, t1.col2, t2.col2, t3.col2
FROM dbo.Table1 t1
LEFT JOIN dbo.Table2 t2 ON t1.col1 = t2.col1
LEFT JOIN dbo.Table3 t3 ON t1.col1 = t3.col1
col1 col2 col2 col2
29 4133 Adam Helen
29 4133 Adam Joice
29 4133 Bob Helen
29 4133 Bob Joice
29 4133 Chris Helen
29 4133 Chris Joice
29 4133 Dave Helen
29 4133 Dave Joice
33 9375 Eddie Karen
33 9375 Eddie Laura
33 9375 Eddie Mary
33 9375 Frank Karen
33 9375 Frank Laura
33 9375 Frank Mary
SQL FIDDLE LINK
Can't you just LEFT JOIN all the tables?
Eg:
SELECT t1.C1, t1.C2,t2.C2, t3.C2
FROM Table1 t1
LEFT JOIN Table2 t2 ON t1.C1 = t2.C1
LEFT JOIN Table3 t3 on t1.C1 = t3.C1
EDIT :
Based on your edit, and hoping things are ordered:
SELECT t1.C1, t1.C2, t2.C2, t3.C2
FROM Table1 t1
LEFT JOIN (
SELECT C1, C2, row_number() OVER (ORDER BY C1, C2) AS row_num
FROM Table2
) t2 ON t1.C1 = t2.C1
LEFT JOIN (
SELECT C1, C2, row_number() OVER (ORDER BY C1, C2) AS row_num
FROM Table3
) t3 ON t1.C1 = t3.C1 AND t3.row_num = t2.row_num
EDIT 2 :
Assuming everything is in order, (Row_Number and Dense_Rank):
SELECT t1.C1, t1.C2, t2.C2, t3.C2
FROM Table1 t1
LEFT JOIN (
SELECT C1, C2, DENSE_RANK() OVER (
ORDER BY C1
) AS group_num, row_number() OVER (
PARTITION BY C1 ORDER BY C1, C2
) AS row_num
FROM Table2
) t2 ON t1.C1 = t2.C1
LEFT JOIN (
SELECT C1, C2, DENSE_RANK() OVER (
ORDER BY C1
) AS group_num, row_number() OVER (
PARTITION BY C1 ORDER BY C1, C2
) AS row_num
FROM Table3
) t3 ON t1.C1 = t3.C1 AND t3.group_num = t2.group_num AND t3.row_num = t2.row_num
EDIT 3:
Dense_Rank, Row_Number, Union
SELECT t1.C1 AS T1C1, t1.C2 AS T1C2, t2.C2 AS T2C2, t3.C2 AS T3C3
FROM Table1 t1
LEFT JOIN (
SELECT C1, C2, DENSE_RANK() OVER (
ORDER BY C1
) AS group_num, row_number() OVER (
PARTITION BY C1 ORDER BY C1, C2
) AS row_num
FROM Table2
) t2 ON t1.C1 = t2.C1
LEFT JOIN (
SELECT C1, C2, DENSE_RANK() OVER (
ORDER BY C1
) AS group_num, row_number() OVER (
PARTITION BY C1 ORDER BY C1, C2
) AS row_num
FROM Table3
) t3 ON t1.C1 = t3.C1 AND t3.group_num = t2.group_num AND t3.row_num = t2.row_num
UNION
SELECT t1.C1, t1.C2, t2.C2, t3.C2
FROM Table1 t1
LEFT JOIN (
SELECT C1, C2, DENSE_RANK() OVER (
ORDER BY C1
) AS group_num, row_number() OVER (
PARTITION BY C1 ORDER BY C1, C2
) AS row_num
FROM Table3
) t3 ON t1.C1 = t3.C1
LEFT JOIN (
SELECT C1, C2, DENSE_RANK() OVER (
ORDER BY C1
) AS group_num, row_number() OVER (
PARTITION BY C1 ORDER BY C1, C2
) AS row_num
FROM Table2
) t2 ON t1.C1 = t2.C1 AND t3.group_num = t2.group_num AND t3.row_num = t2.row_num
ORDER BY T1C1, T1C2, T2C2, T3C3
EDIT 4 :
Dense_Rank, Row_Number, Union, SubQuery, Order By
SELECT *
FROM (
SELECT t1.Col1 AS T1C1, t1.Col2 AS T1C2, t2.Col2 AS T2C2, t3.Col2 AS T3C3
FROM Table1 t1
LEFT JOIN (
SELECT Col1, Col2, DENSE_RANK() OVER (
ORDER BY Col1
) AS group_num, row_number() OVER (
PARTITION BY Col1 ORDER BY Col1, Col2
) AS row_num
FROM Table2
) t2 ON t1.Col1 = t2.Col1
LEFT JOIN (
SELECT Col1, Col2, DENSE_RANK() OVER (
ORDER BY Col1
) AS group_num, row_number() OVER (
PARTITION BY Col1 ORDER BY Col1, Col2
) AS row_num
FROM Table3
) t3 ON t1.Col1 = t3.Col1
AND t3.group_num = t2.group_num
AND t3.row_num = t2.row_num
UNION
SELECT t1.Col1, t1.Col2, t2.Col2, t3.Col2
FROM Table1 t1
LEFT JOIN (
SELECT Col1, Col2, DENSE_RANK() OVER (
ORDER BY Col1
) AS group_num, row_number() OVER (
PARTITION BY Col1 ORDER BY Col1, Col2
) AS row_num
FROM Table3
) t3 ON t1.Col1 = t3.Col1
LEFT JOIN (
SELECT Col1, Col2, DENSE_RANK() OVER (
ORDER BY Col1
) AS group_num, row_number() OVER (
PARTITION BY Col1 ORDER BY Col1, Col2
) AS row_num
FROM Table2
) t2 ON t1.Col1 = t2.Col1
AND t3.group_num = t2.group_num
AND t3.row_num = t2.row_num
) AS SUB
ORDER BY T1C1, T1C2
, CASE WHEN T2C2 IS NULL THEN 1 ELSE 0 END
, CASE WHEN T3C3 IS NULL THEN 1 ELSE 0 END
SQL Fiddle Link
Related
I have this column in my table and I want to get all skipped transactions (which is a varchar)
SA1
SA3
SA50
SA999
I'm trying to get
SA2
SA4 to SA49
SA51 to SA998
EDIT: I've checked the actual data, found out that it's non-trailing zeroes.
I've done this with PHP. Was wondering if is there a way within SQL to do it?
This might sound dumb, but what I'm trying is to plot it like this (this returns null values)
WITH cte (id) AS (
SELECT ROW_NUMBER() OVER (ORDER BY s1.[object_id])
FROM sys.all_columns AS s1
CROSS JOIN sys.all_columns AS s2
),
table1 AS (
SELECT sNumber as id1 FROM Sales WHERE sNumber LIKE '%SI_'
),
table2 AS (
SELECT sNumber as id1 FROM Sales WHERE sNumber LIKE '%SI__'
),
table3 AS (
SELECT sNumber as id1 FROM Sales WHERE sNumber LIKE '%SI___'
)
SELECT
'SA' + RIGHT('' + CAST(t1.id AS VARCHAR(2)), 1) AS id_missing
FROM cte t1
LEFT JOIN table1 t2
ON t1.id = CAST(RIGHT(t2.id1, 1) AS INT)
WHERE
t1.id < (SELECT MAX(CAST(RIGHT(id1, 1) AS INT)) FROM yourTable1) AND
t2.id1 IS NULL
UNION ALL
SELECT
'SA' + RIGHT('' + CAST(t1.id AS VARCHAR(2)), 2) AS id_missing
FROM cte t1
LEFT JOIN table2 t2
ON t1.id = CAST(RIGHT(t2.id2, 2) AS INT)
WHERE
t1.id < (SELECT MAX(CAST(RIGHT(id2, 2) AS INT)) FROM yourTable2) AND
t2.id2 IS NULL
UNION ALL
SELECT
'SA' + RIGHT('' + CAST(t1.id AS VARCHAR(3)), 3) AS id_missing
FROM cte t1
LEFT JOIN table3 t2
ON t1.id = CAST(RIGHT(t2.id3, 3) AS INT)
WHERE
t1.id < (SELECT MAX(CAST(RIGHT(id3, 3) AS INT)) FROM yourTable3) AND
t2.id3 IS NULL
You may use a calendar table approach with a left join here:
WITH cte (id) AS (
SELECT ROW_NUMBER() OVER (ORDER BY s1.[object_id])
FROM sys.all_columns AS s1
CROSS JOIN sys.all_columns AS s2
)
SELECT
'SA' + RIGHT('000' + CAST(t1.id AS VARCHAR(3)), 3) AS id_missing
FROM cte t1
LEFT JOIN yourTable t2
ON t1.id = CAST(RIGHT(t2.id, 3) AS INT)
WHERE
t1.id < (SELECT MAX(CAST(RIGHT(id, 3) AS INT)) FROM yourTable) AND
t2.id IS NULL;
Demo
The idea is to generate a sequence of numbers covering the possible up to 1000 values which might appear in your current table as SAxxx. Then, we left join this calendar table to your current table, on the condition that the numeric portion of the id does not match. All such non matching SAxxx values are then retained in the result set.
I have the following query:
SELECT **top 1** account, date, result
FROM table_1 as t1
JOIN table_2 at t2 ON t1.accountId = t2.frn_accountId
WHERE accountID = 1
ORDER BY date
This query returns the result that I want however I want that result for multiple accountID. They query should return the top 1 value for each accountID.
The query that produce the list of the accountID-s is:
SELECT accountID from lskin WHERE refname LIKE '%BHA%' and isactive = 1
How can I write this query so it can produce the desired result? I have been playing around with CTE but haven't been able to make it correct. It doesn't have to be with CTE, I just thought it can be easier using CTE...
Here is CTE solution.
SELECT *
FROM (SELECT account
, date
, result
, ROW_NUMBER() OVER (PARTITION BY t1.accountId ORDER BY date DESC) AS Rownum
FROM table_1 AS t1
INNER JOIN table_2 AS t2
ON t1.accountId = t2.frn_accountId
INNER JOIN lskin AS l
ON l.accountID = t1.accountID
WHERE l.refname LIKE '%BHA%'
) a
WHERE a.Rownum = 1;
Use max on your date and group by the account, or what ever columns are appropriate.
SELECT
account,
DT = max(date),
result
FROM table_1 as t1
JOIN table_2 as t2 ON t1.accountId = t2.frn_accountId
JOIN lskin as l on l.accountID = t1.accountID
WHERE l.refname like '%BHA%'
GROUP BY
account
,result
If the grouping isn't correct, just join to a sub-query to limit it with max date. Just change the table names as necessary.
SELECT
account,
date,
result
FROM table_1 as t1
JOIN table_2 as t2 ON t1.accountId = t2.frn_accountId
JOIN lskin as l on l.accountID = t1.accountID
INNER JOIN (select max(date) dt, accountID from table_1 group by accountID) tt on tt.dt = t1.accountId and tt.accountId = t1.accountId
WHERE l.refname like '%BHA%'
Ignore the CTE at the top. That's just test data.
/* CTE Test Data */
; WITH table_1 AS (
SELECT 1 AS accountID, 'acc1' AS account UNION ALL
SELECT 2 AS accountID, 'acc2' AS account UNION ALL
SELECT 3 AS accountID, 'acc3' AS account
)
, table_2 AS (
SELECT 1 AS frn_accountID, 'new1' AS result, GETDATE() AS [date] UNION ALL
SELECT 1 AS frn_accountID, 'mid1' AS result, GETDATE()-1 AS [date] UNION ALL
SELECT 1 AS frn_accountID, 'old1' AS result, GETDATE()-2 AS [date] UNION ALL
SELECT 2 AS frn_accountID, 'new2' AS result, GETDATE() AS [date] UNION ALL
SELECT 2 AS frn_accountID, 'mid2' AS result, GETDATE()-1 AS [date] UNION ALL
SELECT 2 AS frn_accountID, 'old2' AS result, GETDATE()-2 AS [date] UNION ALL
SELECT 3 AS frn_accountID, 'new3' AS result, GETDATE() AS [date] UNION ALL
SELECT 3 AS frn_accountID, 'mid3' AS result, GETDATE()-1 AS [date] UNION ALL
SELECT 3 AS frn_accountID, 'old3' AS result, GETDATE()-2 AS [date]
)
, lskin AS (
SELECT 1 AS accountID, 'purple' AS refName, 1 AS isActive UNION ALL
SELECT 2 AS accountID, 'blue' AS refName, 1 AS isActive UNION ALL
SELECT 3 AS accountID, 'orange' AS refName, 0 AS isActive UNION ALL
SELECT 4 AS accountID, 'blue' AS refName, 1 AS isActive
)
,
/* Just use the below and remove comment markers around WITH to build Orders CTE. */
/* ; WITH */
theCTE AS (
SELECT s1.accountID, s1.account, s1.result, s1.[date]
FROM (
SELECT t1.accountid, t1.account, t2.result, t2.[date], ROW_NUMBER() OVER (PARTITION BY t1.account ORDER BY t2.[date]) AS rn
FROM table_1 t1
INNER JOIN table_2 t2 ON t1.accountID = t2.frn_accountID
) s1
WHERE s1.rn = 1
)
SELECT lskin.accountID
FROM lskin
INNER JOIN theCTE ON theCTE.accountid = lskin.accountID
WHERE lskin.refName LIKE '%blue%'
AND lskin.isActive = 1
;
EDITED:
I'm still making a lot of assumptions about your data structure. And again, make sure you're querying what you need. CTEs are awesome, but you don't want to accidentally filter out expected results.
I have a Select with sub selects using Top 1 and where clause.
I tried to optimize the select by doing a Left Join of the sub selects but the query time took longer. Is subselect better in this case? I couldnt post my whole select because it is too long and confidential but I will try to recreate the important part below:
Sub Select
SELECT
(select top 1 colId FROM table1 WHERE col1 = b.Id and col2 = 3 Order by 1) Id3,
(select top 1 colId FROM table1 WHERE col1 = b.Id and col2 = 5 Order by 1) Id5,
(select top 1 colId FROM table1 WHERE col1 = b.Id and col2 = 7 Order by 1) Id7
FROM table2 b
Trying it w/ Left Join
SELECT
t1.colid id3,
t2.colid id5,
t3.colid id7
FROM table2 b
LEFT JOIN (
select colId, col1 FROM table1 WHERE col2 = 3
) t1 ON t1.col1 = b.Id
LEFT JOIN (
select colId, col1 FROM table1 WHERE col2 = 5
) t2 ON t1.col1 = b.Id
LEFT JOIN (
select colId, col1 FROM table1 WHERE col2 = 7
) t3 ON t1.col1 = b.Id
Is there a better way to do this? and why is it the Left join takes longer query time?
You can use ROW_NUMBER:
;WITH cte AS
(
SELECT a.colId,
rn = ROWN_NUMBER() OVER (PARTITION BY a.col2 ORDER BY a.col1)
FROM table1 a
LEFT JOIN table2 b on a.col1 = b.id
WHERE a.col2 IN (3,5,7)
)
SELECT *
FROM cte
WHERE rn = 1
This will give you the first row for each col2 value and you can restrict the values you want to 3,5,7.
I have 2 tables
Table 1:
id name adress
1 John New York
2 Jane London`
... and so on
Table 2:
id fila date
1 43 01/01/2010
1 39 10/01/2011
1 55 23/12/2012
2 10 01/01/2008
2 15 02/02/2010`
.... and so on
I want to get data like this
id fila name adress date
-----------------------------------------
1 55 John New York 23/12/2012
2 15 Jane London 02/02/2010
..... and so on.
Thanks
ok. what you are really looking for is "What is the latest date in table2 for each of my rows in Table1". So to answer the question:
select *
From Table1
inner join (
select id, max(fila) as maxfila
from Table2
group by id
) as maxdates
on Table1.id = maxdates.id
inner join Table2 on Table2.id = maxdates.id AND Table2.fila = maxdates.maxfila
Try this:
;with cte as
(select id, max(fila) maxfila
from table2
group by id)
select t1.id, t1.name, t1.address, t2.fila, t2.date
from table1 t1
left join table2 t2 on t1.id = t2.id
inner join cte c on t1.id = c.id
where t2.fila = c.maxfila
Try this
Select t1.id, t1.name, t1.address, t2.maxfila
from table1 t1
left outer join
(select id, max(fila) maxfila
from table2
group by id) t2
select t1.id, t1.name t1.address, max(t2.fila),
(select top 1 date from table2 order by fila desc where table2.id = t1.id)
from table1 t1 inner join
table2 t2 on t1.id = t2.id
table1, table2 and table3 have different columns but all have an OrderDate column. I want to get a result set of rows from all 3 tables, and I want the final result set to be sorted by OrderDate.
( select * from table1 LEFT join table2 on 0=1 LEFT join table3 on 0=1
where somedate <= table1.orderdate )
union all
( select * from table1 RIGHT join table2 on 0=1 LEFT join table3 on 0=1
where somedate <= table2.orderdate )
union all
( select * from table1 RIGHT join table2 on 0=1 RIGHT join table3 on 0=1
where somedate <= table3.orderdate )
This works, but I want this result set to be ordered by orderdate, so I add:
order by case when table1.orderdate is not null then table1.orderdate
when table2.orderdate is not null then table2.orderdate
else table3.orderdate end
SQL Server returns the error "ORDER BY items must appear in the select list if the statement contains a UNION, INTERSECT or EXCEPT operator."
If I replace
select *
by
select *, table1.orderdate, table2.orderdate, table3.orderdate
I get the same error.
Huh? Thanks.
Try This
Select * from
(( select * from table1 LEFT join table2 on 0=1 LEFT join table3
where somedate <= table1.orderdate )
union all
( select * from table1 RIGHT join table2 on 0=1 LEFT join table3
where somedate <= table2.orderdate )
union all
( select * from table1 RIGHT join table2 on 0=1 RIGHT join table3
where somedate <= table3.orderdate )) A
Order by A.orderdate
I solved the problem using a Common Table Expression, here is what I did:
WITH JoinedTable as
(
( select table1.col1 as t1col1, table2.col2 as t1col2 [etc...]
from table1 LEFT join table2 on 0=1 LEFT join table3 on 0=1
where somedate <= table1.orderdate )
union all
( select table1.col1 as t1col1, table2.col2 as t1col2 [etc...]
from table1 RIGHT join table2 on 0=1 LEFT join table3 on 0=1
where somedate <= table2.orderdate )
union all
( select table1.col1 as t1col1, table2.col2 as t1col2 [etc...]
from table1 RIGHT join table2 on 0=1 RIGHT join table3 on 0=1
where somedate <= table3.orderdate )
)
select *, case when t1orderdate is not null then t1orderdate
when t2orderdate is not null then t2orderdate
else t3orderdate end
AS DateForOrderingResultSet
from JoinedTable order by DateForOrderingResultSet
The
table1.col1 as t1col1, table2.col2 as t1col2 [etc...]
which replaces * cannot be avoided if table1, table2, table3 have identical column names (for example ID) otherwise SQL Server throws an error saying JoinedTable.ID is ambiguous: it has no way to know if this means table1.ID or table2.ID or table3.ID.