Insert into with output clause - sql

INSERT INTO Table1(group, account)
OUTPUT inserted.Id, B.title, B.amount
INTO Table2(id2, title, amount)
SELECT A.*,
B.title,
B.amount,
B.id2
FROM Table1 AS A
LEFT OUTER JOIN
(SELECT title,
amount,
id2
FROM Table2) AS B
ON A.id = B.id2
i'm stuck with this..i have two join tables and what i want is to copy the same set of data from table1 to itself and copy the new id of the newly copied data from table1 to table2 column id2 by using OUTPUT clause.
but now with the query above i cant get through the column that i needed..how can i insert column B.title & B.amount to table2 ?

If table 1 and table 2 have a 1:1 relationship, and no foreign key exists between the two then you could do this in a single statement:
MERGE Table1 AS a
USING
( SELECT A.[group], A.account, B.title, B.amount, B.id2
FROM Table1 AS A
LEFT OUTER JOIN Table2 AS B
ON A.id = B.id2
) AS b
ON 1 = 0
WHEN NOT MATCHED THEN
INSERT ([group], account)
VALUES (b.[group], b.account)
OUTPUT inserted.Id, B.title, B.amount
INTO Table2(id2, title, amount);
Example on SQL Fiddle
Realistically though, if your tables are related they should have a foreign key, and in most cases they won't be 1:1, rather 1:n.
In which case you would still need to use MERGE to caputre both the new ID and the old ID, but you would then need to capture this mapping in a temporary table before performing a second insert to Table2:
DECLARE #Map TABLE (OldID INT NOT NULL, NewID INT NOT NULL);
MERGE Table1 AS a
USING
( SELECT A.ID, A.[group], A.account
FROM Table1 AS A
) AS b
ON 1 = 0
WHEN NOT MATCHED THEN
INSERT ([group], account)
VALUES (b.[group], b.account)
OUTPUT inserted.Id, b.ID
INTO #Map(NewID, OldID);
INSERT Table2 (id2, title, amount)
SELECT m.NewID, b.title, b.amount
FROM #Map AS m
INNER JOIN Table2 AS b
ON b.ID2 = m.OldID;
Example on SQL Fiddle

Related

How can I combine the three queries to one and possibly improve performance

I wish I can get help from this forum, any suggestion is appreciated.
I have a project with some existing sql queries, here are three I think should be combined into only one for the performance optimization purpose.
For the sake of simplicity and confidentiality here are the modified codes:
table1 inner join table2 and left join table22 to generate temp table
from temp table create table cnt with the count for each type
table1 inner join table3, and apply a where clause to generate the final table
.
UPDATE: the key point here is to look for a solution that can minimize the initial data volumn (table1) involved here - which has 8 billion rows, from the where clause added at the end it should be able to restricted to the where clause which will dramatically bring down the size.
UPDATE2: the original queries actually include another left join here, please see the comment below:
create table table1 (id int, region int, typeid int)
insert into table1 (id, region, typeid ) values (1,2,1)
insert into table1 (id, region, typeid ) values (2,3,1)
insert into table1 (id, region, typeid ) values (3,4,2)
insert into table1 (id, region, typeid ) values (4,1,2)
insert into table1 (id, region, typeid ) values (5,1,2)
insert into table1 (id, region, typeid ) values (6,2,4)
create table table2 (id int, type varchar(10))
insert into table2 (id, type) values (1,'A')
insert into table2 (id, type) values (2,'B')
insert into table2 (id, type) values (3,'C')
insert into table2 (id, type) values (4,'D')
table temp is the initial inner join of the two initial tables:
create table temp as
select a.*, b.*
from table1 a inner join table2 b on a.typeid = b.id
table cnt has the count for each type
create table cnt as
select c.type, count(1) as total
from temp c
group by type
Final table:
create table final as
select a.region, a.type
from table1 a inner join cnt c on a.type = c.type where c.total > 2
In the given sample data, the final table should contains only records with type = 2 because type2 has count > 2
Kindly keep in mind the current table1 and table2 contain millions of rows.
Thank you very much.
You can use CTEs. So one simplification is:
with cnt as (
select ?.type, count(*) as total
from table1 a inner join
table2 b
on a.typeid = b.id
group by a.type
select a.region, a.type
from table1 a inner join
cnt c
on a.type = c.type
where c.total > 2;
table2 doesn't contribute to the count, so this can be simplified to:
with cnt as (
select ?.typeid, count(*) as total
from table1 a
group by a.typeid
select a.region, a.typeid
from table1 a inner join
cnt c
on a.typeid = c.typeid
where c.total > 2;
And if your window functions further simplifies this:
select a.*
from (select a.*, count(*) over (partition by a.typeid) as cnt
from table1 a
) a
where cnt > 2;
This will be faster i believe if you have partition over typeid of table 1:
create table #table1 (id int, region int, typeid int)
insert into #table1 (id, region, typeid ) values (1,2,1)
insert into #table1 (id, region, typeid ) values (2,3,1)
insert into #table1 (id, region, typeid ) values (3,4,2)
insert into #table1 (id, region, typeid ) values (4,1,2)
insert into #table1 (id, region, typeid ) values (5,1,2)
insert into #table1 (id, region, typeid ) values (6,2,4)
--exact your request:
select t.region, t.typeid from #table1 t where t.typeid in (select t.typeid from #table1 t group by t.typeid having count(t.typeid) > 2)
--...and if you need group:
select t.region, t.typeid, count(*)recordcount from #table1 t where t.typeid in (select t.typeid from #table1 t group by t.typeid having count(t.typeid) > 2) group by t.region, t.typeid
drop table #table1

How to combine two different tables with different columns

I have a situation where I need to combine two different tables with different columns.
DDL:
create table tableA ([timestamp] datetime, [source] char(1), [description] varchar(20));
insert into tableA values
('2018-10-12', 'a', 'first day'),
('2018-10-13', 'b', 'alarms'),
('2018-10-14', 'c', 'processing');
create table tableB ([timestamp] datetime, entity varchar(20));
insert into tableB values
('2018-10-12', 'new env'),
('2018-10-13', 'resource'),
('2018-10-18', 'integrated');
I have different columns in two different tables. And I need to combine it as shown in screenshot using SQL.
Use union all
select a.timestamp, a.source,a.description,b.entity
from tableA a left join tableB b on a.timestamp=b.timestamp
where b.timestamp is not null
union all
select b.timestamp, a.source,a.description,b.entity
from tableA a right join tableB b on a.timestamp=b.timestamp
where a.timestamp is null
You can use INNER JOIN for this
SELECT a.TimeStamp, a.Source, a.Description, b.Entity
FROM TableA a
LEFT JOIN Tableb b ON a.TimeStamp=b.TimeStamp;
UNION
SELECT a.TimeStamp, a.Source, a.Description, b.Entity
FROM TableA a
RIGHT JOIN Tableb b ON a.TimeStamp=b.TimeStamp;
You need to use full join. Try this query:
select coalesce (a.timestamp, b.timestamp), source, description, entity
from tableA a
full join tableB b on a.timestamp = b.timestamp
Demo
Use Below code
SELECT isnull(t1.TimeStamp, t2.TimeStamp) TimeStamp, t1.source,t1.description, t2.entity from table1 t1 FULL OUTER JOIN table2 t2 on t1.id=t2.id

Join tables that might not have records

I have two tables, tableA which has a list of records and tableB which has restrictions (if any). How can I join the tables that will essentially be an inner join if there are records in tableB or no join if tableB is empty?
ie:
tableA
id | name
1 | val1
2 | val2
tableB (with restrictions)
id | name | userID
1 | val1 | 123
OR tableB (no restrictions)
id | name | userID
is this possible? My attempt below:
SELECT a.*
FROM tableA a
INNER JOIN (CASE WHEN select 1 from tableB = 1 THEN tableB ELSE tableA END) b
ON a.id = b.id
where userID = XXX
EDIT: There is a check on tableB
Just use a left join
SELECT a.*
FROM tableA a
LEFT JOIN tableB b = ON a.id = b.id and b.userid = xxx
I'm not seeing any complexity beyond that at present - given the simplicity of the statement in the original question, I am wondering if you are putting WHERE predicates against the table B - if you are, they need to be in the ON clause of the join
Edited to include your where clause moved.
For empty rows, u can use same query for both restrictions.
I guess you are usign SQL server
Use left join to pull rows if table b is blank
select a.id,a.name
from tableA a left join tableB on a.id = b.id
Demo:
declare #tableA table (id int, name varchar(10))
insert into #tableA
select 1, 'name'
union all
select 2,'name1'
union all
select 3,'name2'
declare #tableb table (id int, name varchar(10))
select a.id,a.name
from #tableA a left join #tableb b on a.id = b.id
this will pull all records from tableA if tableB is empty and only matching records if it is not:
select a.id, a.name
from tableA a
join tableB on a.id = b.id
where exists (select 1 from tableB)
union all
select a.id, a.name
from tableA a
where not exists (select 1 from tableB)

How to left join to first row in SQL Server

How to left join two tables, selecting from second table only the first row?
My question is a follow up of:
SQL Server: How to Join to first row
I used the query suggested in that thread.
CREATE TABLE table1(
id INT NOT NULL
);
INSERT INTO table1(id) VALUES (1);
INSERT INTO table1(id) VALUES (2);
INSERT INTO table1(id) VALUES (3);
GO
CREATE TABLE table2(
id INT NOT NULL
, category VARCHAR(1)
);
INSERT INTO table2(id,category) VALUES (1,'A');
INSERT INTO table2(id,category) VALUES (1,'B');
INSERT INTO table2(id,category) VALUES (1,'C');
INSERT INTO table2(id,category) VALUES (3,'X');
INSERT INTO table2(id,category) VALUES (3,'Y');
GO
------------------
SELECT
table1.*
,FirstMatch.category
FROM table1
CROSS APPLY (
SELECT TOP 1
table2.id
,table2.category
FROM table2
WHERE table1.id = table2.id
ORDER BY id
)
AS FirstMatch
However, with this query, I get inner join results. I want to get left join results. The tabel1.id in desired results should have '2' with NULL. How to do it?
use row_number and left join
with cte as(
select id,
category,
row_number() over(partition by id order by category) rn
from table2
)
select t.id, cte.category
from table1 t
left outer join cte
on t.id=cte.id and cte.rn=1
OUTPUT:
id category
1 A
2 (null)
3 X
SQLFIDDLE DEMO
select table1.id,
(SELECT TOP 1 category FROM table2 WHERE table2.id=table1.id ORDER BY category ASC) AS category
FROM table1
SELECT table1.id ,table2.category
FROM table1 Left join table2
on table1.id = table2.id
where table2.category = ( select top 1 category from table2 t where table1.id = t.id)
OR table2.category is NULL
Following the comment of t-clausen.dk this does the job:
change CROSS APPLY to OUTER APPLY

Select rows from SQL table where ALL of a set of parameters exist in joined table

Given the following SQL Server schema:
CREATE TABLE #TableA (Id int);
CREATE TABLE #TableB (Id int, TableA_Id int, Status_Id int)
CREATE TABLE #Statuses (Id int)
SELECT
*
FROM
#TableA AS A
INNER JOIN #TableB AS B
ON A.Id = B.TableA_Id
INNER JOIN #Statuses AS S
ON B.Status_Id = S.Id
How can I get all the rows in TableA for which there are at least one entry of each of the rows in dbo.Statuses?
For example, in the following set of data only row 2 of #TableA should be returned:
#TableA
[1]
[2]
[3]
[4]
#Statuses
[1]
[2]
[3]
#TableB
[1][1][1]
[2][1][3]
[3][2][1]
[4][2][2]
[5][2][3]
[6][3][1]
SELECT B.TableA_Id
FROM #Statuses S
LEFT JOIN #TableB B
ON B.Status_Id = S.Id
GROUP BY B.TableA_Id
HAVING COUNT(DISTINCT S.Id) = COUNT(DISTINCT B.Status_Id)
You can do this with a "simple" where clause. Assuming that the status column in #TableB only has valid status values:
select a.*
from #TableA a
where (select count(*) from statuses) =
(select count(distinct status) from #TableB b where b.TableA_Id = a.id);
You can relax this assumption. Here is one way:
select a.*
from #TableA a join
(select count(*) as numstatuses from #statuses) s
where (select count(distinct b.status)
from #TableB b join
#statuses s
on b.id = s.id
where b.TableA_Id = a.id
) = numstatuses
SQL Fiddle:
SELECT TableA_Id
FROM TableB
GROUP BY TableA_Id
HAVING COUNT(*) >= (SELECT COUNT(*) FROM Statuses);