How to merge two tables data in one using SQL Server 2008 - sql

I have two tables
Table #1: tbl_test1
id | product1 | price1|
---+----------+-------+
1 | A | 200 |
2 | B | 250 |
3 | C | 300 |
Table #2 : tbl_test2
id | product2 | price2|
---+----------+-------+
40 | P | 200 |
20 | Q | 250 |
and I want to result in my given format
id | product1 | price1|id | product2 | price2|
---+----------+-------+---+----------+-------+
1 | A | 200 |50 | P | 200 |
2 | B | 250 |40 | Q | 250 |
3 | C | 300 | | | |
Please help...

I don't know what's your end game but if you just want to display your data side by side you need to use FULL JOIN. Additionally, you have to add a ROW_NUMBER for each of your tables:
WITH CteTest1 AS(
SELECT *,
rn = ROW_NUMBER() OVER(ORDER BY id)
FROM #tbl_test1
),
CteTest2 AS(
SELECT *,
rn = ROW_NUMBER() OVER(ORDER BY id)
FROM #tbl_test2
)
SELECT
t1.id, t1.product1, t1.price1,
t2.id, t2.product2, t2.price2
FROM CteTest1 t1
FULL JOIN CteTest2 t2
ON t2.rn = t1.rn
ONLINE DEMO

declare #FirstTable table (StudentId int, SubjectId int)
declare #SecondTable table (MarksId int, RankId int, LastRank int)
insert into #FirstTable values (1, 1)
insert into #FirstTable values (1, 2)
insert into #FirstTable values (1, 3)
insert into #SecondTable values (1, 4, 10)
insert into #SecondTable values (1, 5, 11)
;with XmlTable (RowNumber, StudentId, SubjectId) as
(
select row_number() over(order by StudentId) as RowNumber, * from #FirstTable
)
,TechnicalIdsTable (RowNumber, MarksId, RankId, LastRank) as
(
select row_number() over(order by MarksId) as RowNumber, * from #SecondTable
)
select x.StudentId, x.SubjectId, t.MarksId, t.RankId, t.LastRank from XmlTable x
left join TechnicalIdsTable t
on x.RowNumber = t.RowNumber

Related

Subqueries oracle

I would like to select house_id, house_name, image_name, and size for the lowest image size for each house.
create table house( id int, name varchar(50));
create table image( id int, name varchar(50), house_id int, siz int);
insert into house (id, name) values (1,'house1');
insert into house (id, name) values (2,'house2');
insert into image values (31,'img1',1, 10);
insert into image values (32,'img2',2, 20);
insert into image values (33,'img3',2, 15);
insert into image values (34,'img4',2, 19);
http://sqlfiddle.com/#!4/f86fe/35
Can anybody help me? I tried to prepare some query, but in the last column I always get the null values.
select h.id, h.name, m.mid, i.name
from house h left join (select min(siz) as mid,house_id from image
group by house_id) m on h.id = m.house_id
left join image i on i.id=m.house_id
Results:
| ID | NAME | MID | NAME |
|----|--------|-----|--------|
| 1 | house1 | 10 | (null) |
| 2 | house2 | 15 | (null) |
Two options - one using ROW_NUMBER() analytic function and the other using aggregation functions:
SQL Fiddle
Oracle 11g R2 Schema Setup:
create table house( id int, name varchar(50));
create table image( id int, name varchar(50), house_id int, siz int);
insert into house (id, name) values (1,'house1');
insert into house (id, name) values (2,'house2');
insert into image values (31,'img1',1, 10);
insert into image values (32,'img2',2, 20);
insert into image values (33,'img3',2, 15);
insert into image values (34,'img4',2, 19);
Query 1:
SELECT h.id, h.name, i.siz, i.name AS image_name
FROM house h
LEFT OUTER JOIN (
SELECT i.*,
ROW_NUMBER() OVER (
PARTITION BY house_id
ORDER BY siz
) AS rn
FROM image i
) i
ON ( h.id = i.house_id AND i.rn = 1 )
Results:
| ID | NAME | SIZ | IMAGE_NAME |
|----|--------|-----|------------|
| 1 | house1 | 10 | img1 |
| 2 | house2 | 15 | img3 |
Query 2:
SELECT h.id,
MIN( h.name ) AS name,
MIN( i.siz ) AS siz,
MIN( i.name ) KEEP ( DENSE_RANK FIRST ORDER BY siz ) AS image_name
FROM house h
LEFT OUTER JOIN image i
ON ( h.id = i.house_id )
GROUP BY h.id
Results:
| ID | NAME | SIZ | IMAGE_NAME |
|----|--------|-----|------------|
| 1 | house1 | 10 | img1 |
| 2 | house2 | 15 | img3 |
In Oracle 12c, you can use OUTER APPLY, which is simpler (in my view) than ROW_NUMBER()
SELECT h.id, h.name, i.siz, i.name as image_name
FROM house h
OUTER APPLY ( SELECT i.*
FROM image i
WHERE i.house_id = h.id
ORDER BY i.siz ASC
FETCH FIRST 1 ROW ONLY ) i;
+----+--------+-----+------------+
| ID | NAME | SIZ | IMAGE_NAME |
+----+--------+-----+------------+
| 1 | house1 | 10 | img1 |
| 2 | house2 | 15 | img3 |
+----+--------+-----+------------+

Recursive Iteration in Oracle

I have a table like that:
+----+-----+------+
| id | ord | test |
+----+-----+------+
| 1 | 1 | A |
| 1 | 2 | B |
| 1 | 3 | C |
| 2 | 1 | B |
| 2 | 2 | C |
+----+-----+------+
(Here is some code for creating the data)
drop table temp_test;
create table temp_test (id varchar(20), ord varchar(20), test varchar(20));
insert into temp_test (id,ord,test) values ('1','1','A');
insert into temp_test (id,ord,test) values ('1','2','B');
insert into temp_test (id,ord,test) values ('1','3','C');
insert into temp_test (id,ord,test) values ('2','1','B');
insert into temp_test (id,ord,test) values ('2','2','C');
commit;
How could I get the following result?
+----+-----+-------+
| id | ord | test |
+----+-----+-------+
| 1 | 1 | A |
| 1 | 2 | A_B |
| 1 | 3 | A_B_C |
| 2 | 1 | B |
| 2 | 2 | B_C |
+----+-----+-------+
I have tried using LAG(), something like:
select CONCAT(lag(TEST) over (partition by ID order by ord),TEST) AS TEST from temp_test;
but it does not work recursively.
This code works:
SELECT
R1.*,
( SELECT LISTAGG(test, ';') WITHIN GROUP (ORDER BY ord)
FROM temp_test R2
WHERE R1.ord >= R2.ord
AND R1.ID = R2.ID
GROUP BY ID
) AS WTR_KEYWORD_1
FROM temp_test R1
ORDER BY id, ord;
but it is not performant enough for a larger data set.
Some say the Hierarchical queries are outdated, but they generally perform far better than recursive CTE
SELECT id,
ord,
LTRIM(sys_connect_by_path(test,'_'),'_') as test
FROM temp_test r2 START WITH ord = 1 -- use MIN() to get this if it's not always 1
CONNECT BY PRIOR id = id AND ord = PRIOR ord + 1;
Demo
you can make use of recursive cte to achieve this
with cte(id,ord,test,concat_val)
as (select id,ord,test,test as concat_val
from temp_test
where ord=1
union all
select a.id,a.ord,a.test,b.concat_val||'_'||a.test
from temp_test a
join cte b
on a.id=b.id
and a.ord=b.ord+1
)
select * from cte order by id,ord
Demo here
https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=78baa20f7f364e653899caf63ce7ada2

SQL: Select top 1 of each group returning multiple records for each group

I have a table which looks like this:
| Id | InvestorFundId | Name | AccountKey | AsOfDate | AddedOn |
| 1 | 11111 | Name1| Key1 | 9/5/2018 | 8/5/2018 |
| 2 | 11111 | Name2| Key1 | 9/3/2018 | 8/5/2018 |
| 3 | 22222 | Name3| Key2 | 9/2/2018 | 8/5/2018 |
| 4 | 33333 | Name4| Key3 | 9/2/2018 | 8/5/2018 |
| 5 | 33333 | Name5| Key3 | 9/4/2018 | 8/5/2018 |
I need to be able to return the most recent InvestorFundId, Name, and AccountKey for each group, based on ordered AsOfDate and AddedOn descending.
Expected result should look like this:
| InvestorFundId | Name | AccountKey |
| 11111 | Name1| Key1 |
| 22222 | Name3| Key2 |
| 33333 | Name5| Key3 |
I've looked at some posts but I can't correctly return the rows, here is what I have so far:
SELECT Name, AccountKey, H.InvestorFundId FROM
[Investor.Fund.History] H
CROSS APPLY(SELECT TOP 1 InvestorFundId
FROM [Investor.Fund.History]
WHERE DataStatusId = 1 AND AsOfYearMonth <= 201806
ORDER BY AsOfDate DESC, AddedOn DESC) HS
ORDER BY H.InvestorFundId
But this is returning me multiple records for each group instead of the most recent.
I just want to add that the current implementation uses ROW_NUMBER but it is too slow, so I am investigating into other solutions.
Thank you
NOTE: I know that I have two extra columns in my where, I just opted to exclude those from the diagram but you should be able to get the gist
You can use a CTE to get the data you need.
USE tempdb;
GO
DECLARE #table TABLE (Id INT IDENTITY(1, 1), InvestorFundId INT, Name VARCHAR(50), AccountKey VARCHAR(50), AsOfDate DATE, AddedOn DATE);
INSERT INTO #table VALUES (11111, 'Name1', 'Key1', '9/5/2018', '8/5/2018');
INSERT INTO #table VALUES (11111, 'Name2', 'Key1', '9/3/2018', '8/5/2018');
INSERT INTO #table VALUES (22222, 'Name3', 'Key2', '9/2/2018', '8/5/2018');
INSERT INTO #table VALUES (33333, 'Name4', 'Key3', '9/2/2018', '8/5/2018');
INSERT INTO #table VALUES (33333, 'Name5', 'Key3', '9/4/2018', '8/5/2018');
;WITH CTE AS
(
SELECT InvestorFundId, Name, AccountKey, ROW_NUMBER() OVER (PARTITION BY InvestorFundID ORDER BY AsOfDate DESC) AS RowId FROM #table
)
SELECT InvestorFundId, Name, AccountKey
FROM CTE
WHERE RowId = 1;
Here is a working SQLFiddle
Hope it helps.
You can use WITH TIES and simply apply ROW_NUMBER in the ORDER BY
Select top 1 with ties *
From History
Where DataStatusId = 1 and AsOfYearMonth = 201806
Order by
Row_Number() over (partition by InvestorFundID order by AsOfDate desc)
Or with a sub query
Select *
From (select * , RN= Row_Number() over (partition by InvestorFundID order by AsOfDate desc)
From History
Where DataStatusId = 1 and AsOfYearMonth = 201806) x
Where x.RN = 1
If you find this slower, then we’d need to see the execution plan to determine WHY it’s slow. A non-clustered index on InvestorFundId, AsOfDate desc would make this super fast.
Create nonclustered index indexName on
History (InvestorFundID, AsOfDate desc)

Count Number of Consecutive Occurrence of values in Table

I have below table
create table #t (Id int, Name char)
insert into #t values
(1, 'A'),
(2, 'A'),
(3, 'B'),
(4, 'B'),
(5, 'B'),
(6, 'B'),
(7, 'C'),
(8, 'B'),
(9, 'B')
I want to count consecutive values in name column
+------+------------+
| Name | Repetition |
+------+------------+
| A | 2 |
| B | 4 |
| C | 1 |
| B | 2 |
+------+------------+
The best thing I tried is:
select Name
, COUNT(*) over (partition by Name order by Id) AS Repetition
from #t
order by Id
but it doesn't give me expected result
One approach is the difference of row numbers:
select name, count(*)
from (select t.*,
(row_number() over (order by id) -
row_number() over (partition by name order by id)
) as grp
from t
) t
group by grp, name;
The logic is easiest to understand if you run the subquery and look at the values of each row number separately and then look at the difference.
You could use windowed functions like LAG and running total:
WITH cte AS (
SELECT Id, Name, grp = SUM(CASE WHEN Name = prev THEN 0 ELSE 1 END) OVER(ORDER BY id)
FROM (SELECT *, prev = LAG(Name) OVER(ORDER BY id) FROM t) s
)
SELECT name, cnt = COUNT(*)
FROM cte
GROUP BY grp,name
ORDER BY grp;
db<>fiddle demo
The first cte returns group number:
+-----+-------+-----+
| Id | Name | grp |
+-----+-------+-----+
| 1 | A | 1 |
| 2 | A | 1 |
| 3 | B | 2 |
| 4 | B | 2 |
| 5 | B | 2 |
| 6 | B | 2 |
| 7 | C | 3 |
| 8 | B | 4 |
| 9 | B | 4 |
+-----+-------+-----+
And main query groups it based on grp column calculated earlier:
+-------+-----+
| name | cnt |
+-------+-----+
| A | 2 |
| B | 4 |
| C | 1 |
| B | 2 |
+-------+-----+
I have use Recursive CTE and minimise the use of row_number,also avoid count(*).
I think it will perform better,but in real world it depend what else filter you put to minimise number of rows affected.
If ID is having discreet values then One extra CTE will be use to generate continuous id.
;With CTE2 as
(
select ROW_NUMBER()over(order by id) id, name,1 Repetition ,1 Marker from #t
)
, CTE as
(
select top 1 cast(id as int) id, name,1 Repetition ,1 Marker from CTE2 order by id
union all
select a.id, a.name
, case when a.name=c.name then Repetition +1 else 1 end
, case when a.name=c.name then c.Marker else Marker+1 end
from #t a
inner join CTE c on a.id=c.id+1
)
,CTE1 as
(select *,ROW_NUMBER()over(partition by marker order by id desc)rn from cte c
)
select Name,Repetition from cte1 where rn=1

Dynamically create ranges from numeric sequences

I have a table like the following:
+----+-----+-----+
| ID | GRP | NR |
+----+-----+-----+
| 1 | 1 | 101 |
| 2 | 1 | 102 |
| 3 | 1 | 103 |
| 4 | 1 | 105 |
| 5 | 1-2 | 106 |
| 6 | 1-2 | 109 |
| 7 | 1-2 | 110 |
| 8 | 2 | 201 |
| 9 | 2 | 202 |
| 10 | 3 | 300 |
| 11 | 3 | 350 |
| 12 | 3 | 351 |
| 13 | 3 | 352 |
+----+-----+-----+
I wanted to create a view which groups this list by GRP and concatenates values in NR.
Is it possible to dynamically detect sequences and shorten them into ranges?
Like 1, 2, 3, 5 would become 1-3, 5.
So the result should look like this:
+-----+--------------------+
| GRP | NRS |
+-----+--------------------+
| 1 | 101 - 103, 105 |
| 1-2 | 106, 109 - 110 |
| 2 | 201 - 202 |
| 3 | 300, 350 - 352 |
+-----+--------------------+
What i got now is simply concatenate values, so the table above would become this:
+-----+--------------------+
| GRP | NRS |
+-----+--------------------+
| 1 | 101, 102, 103, 105 |
| 1-2 | 106, 109, 110 |
| 2 | 201, 202 |
| 3 | 300, 350, 351, 352 |
+-----+--------------------+
Here's the actual statement:
DECLARE #T TABLE
(
ID INT IDENTITY(1, 1)
, GRP VARCHAR(10)
, NR INT
)
INSERT INTO #T
VALUES ('1',101),('1',102),('1',103),('1',105)
,('1-2',106),('1-2',109), ('1-2',110)
,('2',201),('2',202)
,('3',300),('3',350),('3',351),('3',352)
SELECT * FROM #T
;WITH GROUPNUMS (RN, GRP, NR, NRS) AS
(
SELECT 1, GRP, MIN(NR), CAST(MIN(NR) AS VARCHAR(MAX))
FROM #T
GROUP BY GRP
UNION ALL
SELECT CT.RN + 1, T.GRP, T.NR, CT.NRS + ', ' + CAST(T.NR AS VARCHAR(MAX))
FROM #T T
INNER JOIN GROUPNUMS CT ON CT.GRP = T.GRP
WHERE T.NR > CT.NR
)
SELECT NRS.GRP, NRS.NRS
FROM GROUPNUMS NRS
INNER JOIN (
SELECT GRP, MAX(RN) AS MRN
FROM GROUPNUMS
GROUP BY GRP
) R
ON NRS.RN = R.MRN AND NRS.GRP = R.GRP
ORDER BY NRS.GRP
Can anyone tell me if it's easily possible to do something like that?
Would be great if anyone has an idea and would like to share it.
SQLFiddle demo
with TRes
as
(
select T.GRP,T.NR NR,
CASE WHEN T1.NR IS NULL and T2.NR is null
THEN CAST(T.NR as VARCHAR(MAX))
WHEN T1.NR IS NULL and T2.NR IS NOT NULL
THEN '-'+CAST(T.NR as VARCHAR(MAX))
WHEN T1.NR IS NOT NULL and T2.NR IS NULL
THEN CAST(T.NR as VARCHAR(MAX))+'-'
END AS NR_GRP
from T
left join T T1 on T.Grp=T1.Grp and t.Nr+1=t1.Nr
left join T T2 on T.Grp=T2.Grp and t.Nr-1=t2.Nr
WHERE T1.NR IS NULL or T2.NR IS NULL
)
SELECT
GRP,
REPLACE(
substring((SELECT ( ',' + NR_GRP)
FROM TRes t2
WHERE t1.GRP = t2.GRP
ORDER BY
GRP,
NR
FOR XML PATH( '' )
), 2, 10000 )
,'-,-','-')
FROM TRes t1
GROUP BY GRP
Please check my try:
DECLARE #T TABLE
(
ID INT IDENTITY(1, 1)
, GRP VARCHAR(10)
, NR INT
)
INSERT INTO #T
VALUES ('1',101),('1',102),('1',103),('1',105)
,('1-2',106),('1-2',109), ('1-2',110)
,('2',201),('2',202)
,('3',300),('3',350),('3',351),('3',352)
SELECT * FROM #T
;WITH T1 as
(
SELECT GRP, NR, ROW_NUMBER() over(order by GRP, NR) ID FROM #T
)
,T as (
SELECT *, 1 CNT FROM T1 where ID=1
union all
SELECT b.*, (case when T.NR+1=b.NR and T.GRP=b.GRP then t.CNT
else T.CNT+1 end)
from T1 b INNER JOIN T on b.ID=T.ID+1
)
, TN as(
select *,
MIN(NR) over(partition by GRP, CNT) MinVal,
MAX(NR) over(partition by GRP, CNT) MaxVal
From T
)
SELECT GRP, STUFF(
(SELECT distinct ','+(CASE WHEN MinVal=MaxVal THEN CAST(MinVal as nvarchar(10)) ELSE CAST(MinVal as nvarchar(10))+'-'+cast(MaxVal as nvarchar(10)) END)
FROM TN b where b.GRP=a.GRP
FOR XML PATH(''),type).value('.','nvarchar(max)'),1,1,'') AS [ACCOUNT NAMES]
FROM TN a GROUP BY GRP