Related
I have two tables that I want join as follows:
Table 1
Code1 | Code2 | Date(1) | Amount(1)
A | AA | 201802 | 100
A | AA | 201803 | 50
A | AA | 201804 | 30
Table 2
Code1 | Code2 | Date(2) | Amount(2)
A | AA | 201801 | 20
A | AA | 201802 | 10
A | AA | 201803 | 10
And I want the resulting table to look like this:
Result
Code1 | Code2 | Date(1) | Date(2) | Amount(1) | Amount(2)
A | AA | NULL | 201801 | NULL | 20
A | AA | 201802 | 201802 | 100 | 10
A | AA | 201803 | 201803 | 50 | 10
A | AA | 201804 | NULL | 30 | NULL
So I need to join these two tables
on table1.Code1 = table2.Code1 AND table1.Code2 = table2.Code2 AND table1.Date(1) = table2.Date(2)
But I also want the rows where the dates don't match with a null is the columns related to the non matching table (such as the row for Date(1) = 201804 in my example).
I have tried joining that two tables with left, right and outer join but I still am not successful in getting the rows with the nulls (probably because Code1 and Code2 don't exist for that particular missing row)
Maybe a cross apply could work, but I am not sure how to execute it.
I want the most efficient way in terms of performance because this is a part of a big query containing lots of data and lots of calculations.
UPDATE:
The code I used is:
Select table1.Code 1, table1.Code2, Table1.Date(1), table2.Date(2), table1.Amount(1), table2.amount(2)
FROM Table1
Full Outer Join
table2 ON
table1.Code1 = table2.Code1
AND table1.Code2 = table2.Code2
AND table1.date(1) = table2.date(2)
Which gives me the following result:
Code1 | Code2 | Date(1) | Date(2) | Amount(1) | Amount(2)
A | AA | 201802 | 201802 | 100 | 10
A | AA | 201803 | 201803 | 50 | 10
Which is missing these two rows:
A | AA | NULL | 201801 | NULL | 20
A | AA | 201804 | NULL | 30 | NULL
You may try this.
--sample dataset
DECLARE #tab1 as table (
Code1 varchar(10),
Code2 varchar(10),
Date1 int,
Amount1 int )
insert into #tab1
values
('A', 'AA', 201802, 100),
('A', 'AA', 201803, 50),
('A', 'AA', 201804, 30),
('B', 'AA', 201802, 100) --additional
DECLARE #tab2 as table (
Code1 varchar(10),
Code2 varchar(10),
Date2 int,
Amount2 int )
insert into #tab2
values
('A', 'AA', 201802, 100),
('A', 'AA', 201803, 50),
('A', 'AA', 201801, 30)
query
SELECT *
FROM (
select
coalesce(table1.Code1,table2.Code1) as Code1,
coalesce(table1.Code2,table2.Code2) as Code2,
table1.Date1,
table2.Date2,
table1.Amount1,
table2.amount2
FROM #tab1 as Table1
Full Outer Join #tab2 as table2 ON
table1.Code1 = table2.Code1
AND table1.Code2 = table2.Code2
AND table1.date1= table2.date2
) as t1
CROSS APPLY ( --to exclude records not matched by "Code 1 and Code 2"
SELECT top 1
Code1
FROM #tab2 as t
where t.Code1 = t1.Code1
and t.Code2 = t1.Code2
) as c
ORDER BY t1.Date1
or like this:
select
coalesce(table1.Code1,table2.Code1) as Code1,
coalesce(table1.Code2,table2.Code2) as Code2,
table1.Date1,
table2.Date2,
table1.Amount1,
table2.amount2
FROM #tab1 as Table1
Full Outer Join #tab2 as table2 ON
table1.Code1 = table2.Code1
AND table1.Code2 = table2.Code2
AND table1.date1= table2.date2
where exists (select null --to exclude records not matched by "Code 1 and Code 2"
from #tab2 as t2
where coalesce(table1.Code1,table2.Code1) = t2.Code1
and coalesce(table1.Code2,table2.Code2) = t2.Code2)
ORDER BY table1.Date1
My suggested solution involves a full join and another join to a derived table that contains all the combinations of code1 and code2 that exists in both tables, using the intersect operator.
First, create and populate sample data (Please save us this step in your future questions):
DECLARE #T1 AS TABLE
(
Code1 char(1),
Code2 char(2),
Date1 char(6),
Amount1 int
)
DECLARE #T2 AS TABLE
(
Code1 char(1),
Code2 char(2),
Date2 char(6),
Amount2 int
)
INSERT INTO #T1 (Code1, Code2, Date1, Amount1) VALUES
('A', 'AA', '201802', 100)
,('A', 'AA', '201803', 50)
,('A', 'AA', '201804', 30)
,('B', 'AA', '201802', 30); -- Note: Added to the original sample data
INSERT INTO #T2 (Code1, Code2, Date2, Amount2) VALUES
('A', 'AA', '201801', 20)
,('A', 'AA', '201802', 10)
,('A', 'AA', '201803', 10)
,('A', 'AB', '201802', 10); -- Note: Added to the original sample data
The query:
SELECT ISNULL(T1.Code1, T2.Code1) As Code1,
ISNULL(T1.Code2, T2.Code2) As Code2,
Date1, Date2, Amount1, Amount2
FROM #T1 As T1
FULL JOIN #T2 As T2
ON T1.Code1 = T2.Code1
AND T1.Code2 = T2.Code2
AND T1.Date1 = T2.Date2
-- Remove this next join if you want to get rows where codes don't match
JOIN (
SELECT Code1, Code2
FROM #T1
INTERSECT
SELECT Code1, Code2
FROM #T2
) As CommonCodes
ON CommonCodes.Code1 = ISNULL(T1.Code1, T2.Code1)
AND CommonCodes.Code2 = ISNULL(T1.Code2, T2.Code2)
ORDER BY Date1
Results:
Code1 Code2 Date1 Date2 Amount1 Amount2
A AA NULL 201801 NULL 20
A AA 201802 201802 100 10
A AA 201803 201803 50 10
A AA 201804 NULL 30 NULL
You can see a live demo on rextester.
Your updated query should work if you ISNULL the CodeX columns.
declare #t1 table (Code1 varchar(4), Code2 varchar(4), Date1 date, Amount1 int)
declare #t2 table (Code1 varchar(4), Code2 varchar(4), Date2 date, Amount2 int)
insert into #t1
values
('A', 'AA', '2018-02-01', 100 ),
('A', 'AA', '2018-03-01', 50 ),
('A', 'AA', '2018-04-01', 30 )
insert into #t2
values
('A', 'AA', '2018-01-01', 20 ),
('A', 'AA', '2018-02-01', 10 ),
('A', 'AA', '2018-03-01', 10 )
SELECT
code1
,code2
,date1
,date2
,amount1
,amount2
FROM (
SELECT code1, code2 FROM #t1
INTERSECT
SELECT code1, code2 FROM #t2
) t0
CROSS APPLY (
SELECT
date1, date2, amount1, amount2
FROM #t1 t1
FULL OUTER JOIN #t2 t2 ON t1.Code1 = t2.Code1 and t1.Code2 = t2.Code2 and date1 = date2
WHERE
t0.code1 = isnull(t1.Code1, t2.code1)
and t0.code2 = isnull(t1.Code2, t2.code2)
) tt
ORDER BY
date1, date2
I'm trying to join from a table where the tables and fields are defined within the data instead of keys. So here is what I have
Table Root:
ID | Table | Field
---+---------+-----------
1 | Tab1 | Field1
2 | Tab2 | Field2
3 | Tab1 | Field2
4 | Tab3 | Field4
5 | Tab1 | Field1
Tab1
ID | Field1
---+---------
1 | A
2 | B
3 | C
4 | D
Tab2
ID | Field1 |Field2
---+--------+-----------
1 | X | Bla
2 | Y | 123
3 | Z | 456
Tab3 does not exist
I'd like to have a result like that one:
ID | Value
---+---------
1 | A
2 | 123
3 | NULL -- Field does not match
4 | NULL -- Tables does not exist
5 | NULL -- ID does not exist
Basicly trying to join using the the ID trageting a dynamic table and field.
My Starting Point is somehwere around Here, but this is just for a single specific table. I can't figure out how to join dynamicly or if it even possible without dynamic sql like exec.
you could solve this with a case expression and subqueries, like this example
declare #root table (id int, [table] varchar(10), Field varchar(10))
declare #tab1 table (id int, Field1 varchar(10))
declare #tab2 table (id int, Field1 varchar(10), Field2 varchar(10))
insert into #root (id, [table], Field)
values (1, 'Tab1', 'Field1'), (2, 'Tab2', 'Field2'), (3, 'Tab1', 'Field2'), (4, 'Tab3', 'Field4'), (5, 'Tab1', 'Field1')
insert into #tab1 (id, Field1)
values (1, 'A'), (2, 'B'), (3, 'C'), (4, 'D')
insert into #tab2 (id, Field1, Field2)
values (1, 'X', 'Bla'), (2, 'Y', '123'), (3, 'Z', '456')
select r.id,
case when r.[Table] = 'Tab1' and r.Field = 'Field1' then (select t1.Field1 from #tab1 t1 where t1.ID = r.ID)
when r.[Table] = 'Tab2' and r.Field = 'Field1' then (select t2.Field1 from #tab2 t2 where t2.id = r.id)
when r.[Table] = 'Tab2' and r.Field = 'Field2' then (select t2.Field2 from #tab2 t2 where t2.id = r.id)
end as Value
from #root r
the result is
id Value
-- -------
1 A
2 123
3 null
4 null
5 null
I need to join multiple tables in SQL Server with a common column dates but I want to avoid repeating the values from the different tables when merge.
drop table if exists #d, #t1, #t2
create table #d (DataDate date)
create table #t1 (DataDate date, Value1 float, Value2 float)
create table #t2 (DataDate date, Value3 float, Value4 float)
insert into #d values ('20181201'),('20181202'),('20181203')
insert into #t1 values
('20181201', 3.14, 1.18),
('20181201', 3.135, 1.185),
('20181202', 3.15, 1.19),
('20181203', 3.16, 1.195)
insert into #t2 values
('20181201', 4.14, 2.18),
('20181203', 4.15, 2.19),
('20181203', 4.1, 2.195)
select #d.DataDate,#t1.Value1,#t1.Value2,#t2.Value3,#t2.Value4
from #d
left join #t1 on #d.DataDate = #t1.DataDate
left join #t2 on #d.DataDate = #t2.DataDate
Actual Results
DataDate Value1 Value2 Value3 Value4
12/1/2018 3.14 1.18 4.14 2.18
12/1/2018 3.135 1.185 4.14 2.18
12/2/2018 3.15 1.19 NULL NULL
12/3/2018 3.16 1.195 4.15 2.19
12/3/2018 3.16 1.195 4.1 2.195
Desired Results
DataDate Value1 Value2 Value3 Value4
12/1/2018 3.14 1.18 4.14 2.18
12/1/2018 3.135 1.185 NULL NULL
12/2/2018 3.15 1.19 NULL NULL
12/3/2018 3.16 1.195 4.15 2.19
12/3/2018 NULL NULL 4.1 2.195
Here is a proposed solution.
I have added a third table, just to demonstrate that this could be solved for N tables with a common column.
Prepare demo data:
/* Prepare demo objects */
DROP TABLE IF EXISTS #d, #t1, #t2
CREATE TABLE #d (DataDate date)
CREATE TABLE #t1 (DataDate date, Value1 float, Value2 float)
CREATE TABLE #t2 (DataDate date, Value3 float, Value4 float)
CREATE TABLE #t3 (DataDate date, Value5 float, Value6 float)
/* Insert demo data */
INSERT INTO #d VALUES ('20181201'),('20181202'),('20181203')
INSERT INTO #t1 VALUES
('20181201', 3.14, 1.18),
('20181201', 3.135, 1.185),
('20181202', 3.15, 1.19),
('20181203', 3.16, 1.195)
INSERT INTO #t2 VALUES
('20181201', 4.14, 2.18),
('20181203', 4.15, 2.19),
('20181203', 4.1, 2.195)
INSERT INTO #t3 VALUES
('20181201', 3.14, 1.18),
('20181201', 3.135, 1.185),
('20181202', 3.16, 1.195)
Proposed QUERY Solution:
SELECT
COALESCE(d.DataDate, t1.datadate, t2.datadate, t3.datadate) AS DataDate
, t1.Value1
, t1.Value2
, t2.Value3
, t2.Value4
, t3.Value5
, t3.Value6
FROM
(SELECT
*
, ROW_NUMBER() OVER (PARTITION BY DataDate ORDER BY (SELECT NULL)) AS rn
FROM #d) AS d
FULL JOIN
(SELECT
*
, ROW_NUMBER() OVER (PARTITION BY DataDate ORDER BY (SELECT NULL)) AS rn
FROM #t1) AS t1
ON (t1.DataDate = d.DataDate AND t1.rn = d.rn)
FULL JOIN
(SELECT
*
, ROW_NUMBER() OVER (PARTITION BY datadate ORDER BY (SELECT NULL)) AS rn
FROM #t2) AS t2
ON (t2.DataDate = d.DataDate AND t2.rn = d.rn)
OR (t2.DataDate = t1.DataDate AND t2.rn = t1.rn)
FULL JOIN
(SELECT
*
, ROW_NUMBER() OVER (PARTITION BY datadate ORDER BY (SELECT NULL)) AS rn
FROM #t3) AS t3
ON (t3.DataDate = d.DataDate AND t3.rn = d.rn)
OR (t3.DataDate = t1.DataDate AND t3.rn = t1.rn)
OR (t3.DataDate = t2.DataDate AND t3.rn = t2.rn)
ORDER BY DataDate;
Demo fiddle is posted on db<>fiddle here
Results:
DataDate | Value1 | Value2 | Value3 | Value4 | Value5 | Value6
:------------------ | -----: | -----: | -----: | -----: | -----: | -----:
01/12/2018 00:00:00 | 3.14 | 1.18 | 4.14 | 2.18 | 3.14 | 1.18
01/12/2018 00:00:00 | 3.135 | 1.185 | null | null | 3.135 | 1.185
02/12/2018 00:00:00 | 3.15 | 1.19 | null | null | 3.16 | 1.195
03/12/2018 00:00:00 | 3.16 | 1.195 | 4.15 | 2.19 | null | null
03/12/2018 00:00:00 | null | null | 4.1 | 2.195 | null | null
Note (optional):
You can greately improve performance by introducing indexes.
As a demo, I have added CLUSTERED INDEXES on DateData column and the preformance increase is significant.
/* Add to improve performance */
CREATE CLUSTERED INDEX CI_DataDate ON #d (DataDate);
CREATE CLUSTERED INDEX CI_DataDate ON #t1 (DataDate);
CREATE CLUSTERED INDEX CI_DataDate ON #t2 (DataDate);
CREATE CLUSTERED INDEX CI_DataDate ON #t3 (DataDate);
Use min if you want the min value (or max depending on what you're looking for) associated by date and t1.value1. Your example, the values are not duplicates so distinct will not work
select #d.DataDate,#t1.Value1,min(#t1.Value2),max(#t2.Value3),min(#t2.Value4)
from #d
left join #t1 on #d.DataDate = #t1.DataDate
left join #t2 on #d.DataDate = #t2.DataDate
group by 1,2
if there are exact duplicates that you want to remove then use the following
select distinct #d.DataDate,#t1.Value1,#t1.Value2,#t2.Value3,#t2.Value4
from #d
left join #t1 on #d.DataDate = #t1.DataDate
left join #t2 on #d.DataDate = #t2.DataDate
I have three excel sheet I push them into tables in SQL server and I need to join these table. However, I believe - as I have tried already - normal join wouldn't work. I have programming background but not that much with SQL.
Table1
ID Data_column reference_number
1 some data 1528,ss-456
2 some data 9523
3 some data ss-952
4 some data null
Table2
ID Data_column
ss-456 some data
ss-952 some data
Table3
ID Data_column
1528 some data
9523 some data
In the case below How I will be able to join this raw on both table.
Table1
ID Data_column reference_number
1 some data 1528,ss-456
declare #t1 as table(
id int
,data_column varchar(20)
,reference_number varchar(20)
)
declare #t2 as table(
id varchar(20)
,data_column varchar(20)
)
declare #t3 as table(
id varchar(20)
,data_column varchar(20)
)
insert into #t1 values(1,'some data','1528,ss-456'),(2,'some data','9523'),(3,'some data','ss-952'),(4,'some data',null);
insert into #t2 values('ss-456','some data'),('ss-952','some data');
insert into #t3 values(1528,'some data'),(9523,'some data');
Quick solution
select * from #t1 t1
left outer join #t2 t2 on t1.reference_number like '%'+t2.id or t1.reference_number like t2.id+'%'
left outer join #t3 t3 on t1.reference_number like '%'+t3.id or t1.reference_number like t3.id+'%'
Result (left join):
id data_column reference_number id data_column id data_column
1 some data 1528,ss-456 ss-456 some data 1528 some data
2 some data 9523 NULL NULL 9523 some data
3 some data ss-952 ss-952 some data NULL NULL
4 some data NULL NULL NULL NULL NULL
You can change 'left outer join' to 'inner join' for exact match.
Clumsy design, clumsy solution:
SELECT *
FROM Table1
INNER JOIN Table2 ON ',' + Table1.reference_number + ',' LIKE '%,' + Table2.ID + ',%'
INNER JOIN Table3 ON ',' + Table1.reference_number + ',' LIKE '%,' + Table3.ID + ',%'
You must append leading and trailing commas to make sure that, for example, 1528,ss-456asdf does not match %ss-456%.
I see two problems here. First is the inconsistent type of ID in table 2 and 3 and aggregation of referenced keys in table 1. Here is an example how to solve both problems. To split REFERENCE_NUMBER column I used STRING_SPLIT function.
Update:
I added the solution which should work with SQL Server 2012.
I assumed that you wish to join data from table 1 with 2 or 3 depending in existence of this data. This is just my idea what you wanted to achive.
-- data preparing
declare #t1 as table(
id int
,data_column varchar(20)
,reference_number varchar(20)
)
declare #t2 as table(
id varchar(20)
,data_column varchar(20)
)
declare #t3 as table(
id int
,data_column varchar(20)
)
insert into #t1 values(1,'some data','1528,ss-456'),(2,'some data','9523'),(3,'some data','ss-952'),(4,'some data',null);
insert into #t2 values('ss-456','some data'),('ss-952','some data');
insert into #t3 values(1528,'some data'),(9523,'some data');
-- Solution example version >= 2016
with base as (
select t1.id,t1.data_column,f1.value from #t1 t1 outer apply string_split(t1.reference_number,',') f1)
select b.id,b.data_column,b.value,t2.data_column from base b join #t2 t2 on b.value = t2.id
union all
select b.id,b.data_column,b.value,t3.data_column from base b join #t3 t3 on try_cast(b.value as int ) = t3.id
union all
select b.id,b.data_column,b.value,null from base b where b.value is null;
-- Solution for SQL Version < 2016
with base as (
select t1.id,t1.data_column,f1.value from #t1 t1 outer apply(
SELECT Split.a.value('.', 'NVARCHAR(MAX)') value
FROM
(
SELECT CAST('<X>'+REPLACE(t1.reference_number, ',', '</X><X>')+'</X>' AS XML) AS String
) AS A
CROSS APPLY String.nodes('/X') AS Split(a)
) f1)
select b.id,b.data_column,b.value,t2.data_column from base b join #t2 t2 on b.value = t2.id
union all
select b.id,b.data_column,b.value,t3.data_column from base b join #t3 t3 on try_cast(b.value as int ) = t3.id
union all
select b.id,b.data_column,b.value,null from base b where b.value is null;
You will require a function to divide the comma separated sting into rows. If you don't have access to thr inbuilt string_split() function (as of mssql 2017 with compatibility of 130) there are several to choose from here
CREATE TABLE table1(
ID INTEGER NOT NULL PRIMARY KEY
,Data_column VARCHAR(10) NOT NULL
,reference_number VARCHAR(11)
);
INSERT INTO table1(ID,Data_column,reference_number) VALUES
(1,'t1somedata','1528,ss-456')
, (2,'t1somedata','9523')
, (3,'t1somedata','ss-952')
, (4,'t1somedata',NULL);
CREATE TABLE table2(
ID VARCHAR(6) NOT NULL PRIMARY KEY
,Data_column VARCHAR(10) NOT NULL
);
INSERT INTO table2(ID,Data_column) VALUES
('ss-456','t2somedata'),
('ss-952','t2somedata');
CREATE TABLE table3(
ID VARCHAR(6) NOT NULL PRIMARY KEY
,Data_column VARCHAR(10) NOT NULL
);
INSERT INTO table3(ID,Data_column) VALUES
('1528','t3somedata'),
('9523','t3somedata');
I have used this splistring function, but you can use almost any of the many freely available.
CREATE FUNCTION dbo.SplitStrings_Moden
(
#List NVARCHAR(MAX),
#Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING AS
RETURN
WITH E1(N) AS ( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1),
E2(N) AS (SELECT 1 FROM E1 a, E1 b),
E4(N) AS (SELECT 1 FROM E2 a, E2 b),
E42(N) AS (SELECT 1 FROM E4 a, E2 b),
cteTally(N) AS (SELECT 0 UNION ALL SELECT TOP (DATALENGTH(ISNULL(#List,1)))
ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E42),
cteStart(N1) AS (SELECT t.N+1 FROM cteTally t
WHERE (SUBSTRING(#List,t.N,1) = #Delimiter OR t.N = 0))
SELECT Item = SUBSTRING(#List, s.N1, ISNULL(NULLIF(CHARINDEX(#Delimiter,#List,s.N1),0)-s.N1,8000))
FROM cteStart s;
This is what the data looks like using the splitstring function:
select *
from table1
cross apply SplitStrings_Moden(reference_number,',')
ID | Data_column | reference_number | Item
-: | :---------- | :--------------- | :-----
1 | t1somedata | 1528,ss-456 | 1528
1 | t1somedata | 1528,ss-456 | ss-456
2 | t1somedata | 9523 | 9523
3 | t1somedata | ss-952 | ss-952
4 | t1somedata | null | null
and now joining to the other tables:
select
*
from (
select *
from table1
cross apply SplitStrings_Moden(reference_number,',')
) t1
left join table2 on t1.item = table2.id
left join table3 on t1.item = table3.id
where t1.item is not null
GO
ID | Data_column | reference_number | Item | ID | Data_column | ID | Data_column
-: | :---------- | :--------------- | :----- | :----- | :---------- | :--- | :----------
1 | t1somedata | 1528,ss-456 | 1528 | null | null | 1528 | t3somedata
1 | t1somedata | 1528,ss-456 | ss-456 | ss-456 | t2somedata | null | null
2 | t1somedata | 9523 | 9523 | null | null | 9523 | t3somedata
3 | t1somedata | ss-952 | ss-952 | ss-952 | t2somedata | null | null
db<>fiddle here
TRY THIS: If your reference_number is fixed and always stored IDs upto 2 only then you can go with the below approach
SELECT *
FROM(
SELECT ID,
data_column,
CASE WHEN PATINDEX ( '%,%', reference_number) > 0 THEN
SUBSTRING(reference_number, PATINDEX ( '%,%', reference_number)+1, LEN(reference_number))
ELSE reference_number END AS ref_col
FROM #table1
UNION
SELECT ID,
data_column,
CASE WHEN PATINDEX ( '%,%', reference_number) > 0 THEN
SUBSTRING(reference_number, 0, PATINDEX ( '%,%', reference_number))
END
FROM #table1) t1
LEFT JOIN #table2 t2 ON t2.id = t1.ref_col
LEFT JOIN #table3 t3 ON t3.id = t1.ref_col
WHERE t1.ref_col IS NOT NULL
OUTPUT:
ID data_column ref_col ID Data_column ID Data_column
1 some data 1528 NULL NULL 1528 some data
1 some data ss-456 ss-456 some data NULL NULL
2 some data 9523 NULL NULL 9523 some data
3 some data ss-952 ss-952 some data NULL NULL
4 some data null NULL NULL NULL NULL
You can Implement and get desired result using Substring and charIndex functions on the reference_number.
I upvoted an answer of 'is_oz' since i used his ready made schema to test and build a query for you.
below is the final query i build after several tries i made here:
select * from abc
left join abc2 on abc2.id = case when charindex(',',abc.reference_number) > 0
then substring(abc.reference_number
,charindex(',',abc.reference_number)+1
,len(abc.reference_number)-(charindex(',',abc.reference_number)-1)
)
else abc.reference_number
end
left join abc3 on abc3.id = case when charindex(',',abc.reference_number) > 0
then substring(abc.reference_number
,0
,(charindex(',',abc.reference_number))
)
else abc.reference_number
end
As per your requirement as much as i understand, it is returning all the matched rows from 2 other tables but still i hope this fulfills all the requirements you seek in your question. :)
Giving the following 2 tables:
T1
------------------
From | To | Value
------------------
10 | 20 | XXX
20 | 30 | YYY
30 | 40 | ZZZ
T2
------------------
From | To | Value
------------------
10 | 15 | AAA
15 | 19 | BBB
19 | 39 | CCC
39 | 40 | DDD
What is the best way to get the result below, using T-SQL on SQL Server 2008?
The From/To ranges are sequential (there are no gaps) and the next From always has the same value as the previous To
Desired result
-------------------------------
From | To | Value1 | Value2
-------------------------------
10 | 15 | XXX | AAA
15 | 19 | XXX | BBB
19 | 20 | XXX | CCC
20 | 30 | YYY | CCC
30 | 39 | ZZZ | CCC
39 | 40 | ZZZ | DDD
First I declare data that looks like the data you posted. Please correct me if any assumptions I have made are wrong. Better would be to post your own declaration in the question so we are all working with the same data.
DECLARE #T1 TABLE (
[From] INT,
[To] INT,
[Value] CHAR(3)
);
INSERT INTO #T1 (
[From],
[To],
[Value]
)
VALUES
(10, 20, 'XXX'),
(20, 30, 'YYY'),
(30, 40, 'ZZZ');
DECLARE #T2 TABLE (
[From] INT,
[To] INT,
[Value] CHAR(3)
);
INSERT INTO #T2 (
[From],
[To],
[Value]
)
VALUES
(10, 15, 'AAA'),
(15, 19, 'BBB'),
(19, 39, 'CCC'),
(39, 40, 'DDD');
Here is my select query to generate your expected result:
SELECT
CASE
WHEN [#T1].[From] > [#T2].[From]
THEN [#T1].[From]
ELSE [#T2].[From]
END AS [From],
CASE
WHEN [#T1].[To] < [#T2].[To]
THEN [#T1].[To]
ELSE [#T2].[To]
END AS [To],
[#T1].[Value],
[#T2].[Value]
FROM #T1
INNER JOIN #T2 ON
(
[#T1].[From] <= [#T2].[From] AND
[#T1].[To] > [#T2].[From]
) OR
(
[#T2].[From] <= [#T1].[From] AND
[#T2].[To] > [#T1].[From]
);
Stealing #isme's data setup, I wrote the following:
;With EPs as (
select [From] as EP from #T1
union
select [To] from #T1
union
select [From] from #T2
union
select [To] from #T2
), OrderedEndpoints as (
select EP,ROW_NUMBER() OVER (ORDER BY EP) as rn from EPs
)
select
oe1.EP,
oe2.EP,
t1.Value,
t2.Value
from
OrderedEndpoints oe1
inner join
OrderedEndpoints oe2
on
oe1.rn = oe2.rn - 1
inner join
#T1 t1
on
oe1.EP < t1.[To] and
oe2.EP > t1.[From]
inner join
#T2 t2
on
oe1.EP < t2.[To] and
oe2.EP > t2.[From]
That is, you create a set containing all of the possible end points of periods (EPs), then you "sort" those and assign each one a row number (OrderedEPs).
Then the final query assembles each "adjacent" pair of rows together, and joins back to the original tables to find which rows from each one overlap the selected range.
The below query finds the smallest ranges, then picks the values back out the tables again:
SELECT ranges.from, ranges.to, T1.Value, T2.Value
FROM (SELECT all_from.from, min(all_to.to) as to
FROM (SELECT T1.FROM
FROM T1
UNION
SELECT T2.FROM
FROM T2) all_from
JOIN (SELECT T1.TO
FROM T1
UNION
SELECT T2.FROM
FROM T2) all_to ON all_from.from < all_to.to
GROUP BY all_from.from) ranges
JOIN T1 ON ranges.from >= T1.from AND ranges.to <= T1.to
JOIN T2 ON ranges.from >= T2.from AND ranges.to <= T2.to
ORDER BY ranges.from
Thanks for the answers, but I ended using a CTE, wgich I think is cleaner.
DECLARE #T1 TABLE ([From] INT, [To] INT, [Value] CHAR(3));
DECLARE #T2 TABLE ([From] INT, [To] INT, [Value] CHAR(3));
INSERT INTO #T1 ( [From], [To], [Value]) VALUES (10, 20, 'XXX'), (20, 30, 'YYY'), (30, 40, 'ZZZ');
INSERT INTO #T2 ( [From], [To], [Value]) VALUES (10, 15, 'AAA'), (15, 19, 'BBB'), (19, 39, 'CCC'), (39, 40, 'DDD');
;with merged1 as
(
select
t1.[From] as from1,
t1.[to] as to1,
t1.Value as Value1,
t2.[From] as from2,
t2.[to] as to2,
t2.Value as Value2
from #t1 t1
inner join #T2 t2
on t1.[From] < t2.[To]
and t1.[To] >= t2.[From]
)
,merged2 as
(
select
case when from2>=from1 then from2 else from1 end as [From]
,case when to2<=to1 then to2 else to1 end as [To]
,value1
,value2
from merged1
)
select * from merged2