t-sql insert - select - with parameters - sql

I have a 4 tables. One of the tables we are going to be inserting data into (Table A).
Table A is going to receive misc data from Table B, C, D and also some unknown variable parameter data.
How do I set up the INSERT with a SELECT with also receiving parameters?

Something like this?
Insert INTO TableA (col1, col2,col3,col4)
SELECT b.col1, c.col2, d.col3, #myparam
FROM TableB as b
INNER JOIN TableC as c
ON b.id = c.id
INNER JOIN TableD as d
on c.id = d.id

Something like this:
DECLARE #a int, #b int
SET #a = 5
SET #b = 7
INSERT INTO TableA(Column1, Column2)
SELECT SomeOtherColumn, #a
FROM TableB
UNION
SELECT YetAnotherColumn, #b
FROM TableC

Related

LEFT JOIN with OR clause without UNION

I know this shouldn't happen in a database, but it happened and we have to deal with it. We need to insert new rows into a table if they don't exist based on the values in another table. This is easy enough (just do LEFT JOIN and check for NULL values in 1st table). But...the join isn't very straight forward and we need to search 1st table on 2 conditions with an OR and not AND. So basically if it finds a match on either of the 2 attributes, we consider that the corresponding row in 1st table exists and we don't have to insert a new one. If there are no matches on either of the 2 attributes, then we consider it as a new row. We can use OR condition in the LEFT JOIN statement but from what I understand, it does full table scan and the query takes a very long time to complete even though it yields the right results. We cannot use UNION either because it will not give us what we're looking for.
Just for simplicity purpose consider the scenario below (we need to insert data into tableA).
If(OBJECT_ID('tempdb..#tableA') Is Not Null) Begin
Drop Table #tableA End
If(OBJECT_ID('tempdb..#tableB') Is Not Null) Begin
Drop Table #tableB End
create table #tableA ( email nvarchar(50), id int )
create table #tableB ( email nvarchar(50), id int )
insert into #tableA (email, id) values ('123#abc.com', 1), ('456#abc.com', 2), ('789#abc.com', 3), ('012#abc.com', 4)
insert into #tableB (email, id) values ('234#abc.com', 1), ('456#abc.com', 2), ('567#abc.com', 3), ('012#abc.com', 4), ('345#abc.com', 5)
--THIS QUERY IS CORRECTLY RETURNING 1 RECORD
select B.email, B.id
from #tableB B
left join #tableA A on A.email = B.email or B.id = A.id
where A.id is null
--THIS QUERY IS INCORRECTLY RETURNING 3 RECORDS SINCE THERE ARE ALREADY RECORDS WITH ID's 1 & 3 in tableA though the email addresses of these records don't match
select B.email, B.id
from #tableB B
left join #tableA A on A.email = B.email
where A.id is null
union
select B.email, B.id
from #tableB B
left join #tableA A on B.id = A.id
where A.id is null
If(OBJECT_ID('tempdb..#tableA') Is Not Null) Begin
Drop Table #tableA End
If(OBJECT_ID('tempdb..#tableB') Is Not Null) Begin
Drop Table #tableB End
The 1st query works correctly and only returns 1 record, but the table size is just few records and it completes under 1 sec. When the 2 tables have thousands or records, the query may take 10 min to complete. The 2nd query of course returns the records we don't want to insert because we consider them existing. Is there a way to optimize this query so it takes an acceptable time to complete?
You are using an anti join, which is another way of writing the straight-forward NOT EXISTS:
where not exists
(
select null
from #tableA A
where A.email = B.email or B.id = A.id
)
I.e. where not exists a row in table A with the same email or the same id. In other words: where not exists a row with the same email and not exists a row with the same id.
where not exists (select null from #tableA A where A.email = B.email)
and not exists (select null from #tableA A where B.id = A.id)
With the appropriate indexes
on #tableA (id);
on #tableA (email);
this should be very fast.
It's hard to tune something you can't see. Another option to get the data is to:
SELECT B.email
, B.id
FROM #TableB B
EXCEPT
(
SELECT B.email
, B.id
FROM #tableB B
INNER JOIN #tableA A
ON A.email = B.email
UNION ALL
SELECT B.email
, B.id
FROM #tableB B
INNER JOIN #tableA A
ON B.id = A.id
)
This way you don't have to use OR, you can use INNER JOIN rather than LEFT JOIN and you can use UNION ALL instead of UNION (though this advantage may well be negated by the EXCEPT). All of which may help your performance. Perhaps the joins can be more efficient when replaced with EXISTS.
You didn't mention how this problem occurred (where the data from both tables is coming from, and why they are out of sync when they shouldn't be), but it would be preferable to fix it at the source.
No the query returns correctly 3 rows
because
select B.email, B.id
from #tableB B
left join #tableA A on A.email = B.email
where A.id is null
Allone reurns the 3 rows.
For your "problemm"
select B.email, B.id
from #tableB B
left join #tableA A on A.email = B.email or B.id = A.id
where A.id is null
will che3kc for every row, if it is true to be included
So for example
('123#abc.com', 1) ('234#abc.com', 1)
as the Ids are the same it will be joined
but when you join by the emails the condition is false and so is included in the result set
You can only use the UNION approach, when you are comparing only the emails or the ids, but with both the queries are not equivalent

is there a syntactic shortcut similar to coalesce for union?

What I'm trying to do is:
select
A.Fuzz
,A.Fizz
,B.Whiz
from A
left outer join B
on A.Fuzzy B=Wuzzy
To replace:
select
A.Fuzz
,A.Fizz
,B.Whiz
from A
left outer join B
on A.Fuzzy B=Wuzzy
UNION ALL
select
B.wuzz
,A.Fizz
,B.Whiz
from A
left outer join B
on A.Fuzzy B=Wuzzy
May be this is something "near" what you think (done on MSSQL)?. Pls in every question post sample data, expected result, etc.
CREATE TABLE A (ID INT, DESC_A VARCHAR(10));
INSERT INTO A VALUES (1,'A');
INSERT INTO A VALUES (2,'B');
CREATE TABLE B (ID INT, DESC_B VARCHAR(10));
INSERT INTO B VALUES (2,'Z');
INSERT INTO B VALUES (3,'Y');
SELECT COALESCE(A.ID, B.ID) AS ID
, A.DESC_A
, B.DESC_B
FROM A
FULL JOIN B ON A.ID = B.ID
Output:
ID DESC_A DESC_B
1 A NULL
2 B Z
3 NULL Y

SQL UPDATE from multiple rows to single row

this illustrates the issue:
CREATE TABLE Table_A (id int, value char)
INSERT INTO Table_A VALUES (1, 'A')
INSERT INTO Table_A VALUES (2, 'B')
CREATE TABLE Table_B (id int, value char)
INSERT INTO Table_B VALUES (1, 'C')
INSERT INTO Table_B VALUES (1, 'D')
If you run
UPDATE a SET a.value = (SELECT b.value FROM Table_B b WHERE a.id = b.id)
FROM Table_A a, Table_B b WHERE a.id = b.id
You get an error saying
Subquery returned more than 1 value. This is not permitted when the
subquery follows =, !=, <, <= , >, >= or when the subquery is used as
an expression.
But if you run this
UPDATE a SET a.value = b.value
FROM Table_A a, Table_B b WHERE a.id = b.id
No error is thrown and you get the row updated, why is that?
Edit:
Sorry guys, you seem to focusing on explaining why the first query gives error, but I think that is obvious and to me that is a desire result (because setting value of Table_A for id 1 to value of Table_B with id 1 is undefined when there are multiple values in Table_B with id 1)
My question is actually asking why the second query does not give you an error, which is causing trouble to me (i.e. I want it to break if I have more than one row with the same id but different values)
You got that error because you are using subquery when you set a new value and the subquery return more than 1 result.
SET a.value = (SELECT b.value FROM Table_B b WHERE a.id = b.id)
It will error when update a value with id = 1, because there is two record that have id = 1 in table b.
So your query will look like this (this is only for illustration and of course will cause an error)
UPDATE a SET a.value = ('C', 'D')
FROM Table_A a, Table_B b WHERE a.id = b.id
When you're using this query
UPDATE a SET a.value = b.value
FROM Table_A a, Table_B b WHERE a.id = b.id
You are join the table a with table b using id field, so the result is
a.id => a.value => b.value : 1 A C
a.id => a.value => b.value : 1 A D
No entry record for id = 2 because there is no matching record in table b.
So your update query will looks like this
UPDATE a SET a.value = 'C'
FROM Table_A a, Table_B b WHERE a.id = 1
UPDATE a SET a.value = 'D'
FROM Table_A a, Table_B b WHERE a.id = 1
Because your subquery will return both more than 1 result. The Assign statement will not accept more than 1 value.
You have to use JOIN
May be something like this
UPDATE A
SET A.value = B.value
FROM Table_A A INNER JOIN Table_B B ON A.id = B.id
FIDDLE DEMO
The reason your second query does not error our is because it will assign the first available B.value to a.value where it satisfies the condition
A.id = B.id
Try the following snippet and you will see how the Update...Set works.
create table #temp (id int, name varchar(100), phone int)
create table #temp1 (id int, phone int)
insert into #temp (id, name)
select 1, 'Mark' union
select 2, 'JOhn' union
select 3, 'Jerry'
insert into #temp1 (id, phone)
select 1, 123456 union
select 1, 222564 union
select 1, 222210
select * from #temp
select * from #temp1
update t
set phone = t1.phone
from #temp1 t1, #temp t
where t.id = t1.id
select * from #temp

Inner join 2 tables but return all if 1 table empty

I have 2 tables say A and B, and I want to do a join on them.
Table A will always have records in it.
When table B has rows in it, I want the query to turn all the rows in which table A and table B matches. (i.e. behave like inner join)
However, if table B is empty, I'd like to return everything from table A.
Is this possible to do in 1 query?
Thanks.
Yes, for results like this, use LEFT JOIN.
Basically what INNER JOIN does is it only returns row where it has atleast one match on the other table. The LEFT JOIN, on the other hand, returns all records on the left hand side table whether it has not match on the other table.
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
I came across the same question and, as it was never answered, I post a solution given to this problem somewhere else in case it helps someone in the future.
See the source.
select *
from TableA as a
left join TableB as b
on b.A_Id = a.A_Id
where
b.A_Id is not null or
not exists (select top 1 A_Id from TableB)
Here is another one, but you need to add one "null" row to table B if it's empty
-- In case B is empty
Insert into TableB (col1,col2) values (null,null)
select *
from TableA as a inner join TableB as b
on
b.A_Id = a.A_Id
or b.A_Id is null
I would use an if-else block to solve it like below:
if (select count(*) from tableB) > 0
begin
Select * from TableA a Inner Join TableB b on a.ID = b.A_ID
end
else
begin
Select * from TableA
end
Try This
SELECT t1.* FROM table1 AS t1 INNER JOIN table2 AS t2 ON t1.something = t2.someotherthing UNION SELECT * FROM table1 WHERE something = somethingelse;
This is solution:
CREATE TABLE MyData(Id INT, Something VARCHAR(10), OwnerId INT);
CREATE TABLE OwnerFilter(OwnerId INT);
SELECT *
FROM
(SELECT NULL AS Gr) AS Dummy
LEFT JOIN OwnerFilter F ON (1 = 1)
JOIN MyData D ON (F.OwnerId IS NULL OR D.OwnerId = F.OwnerId);
Link to sqlfiddle: http://sqlfiddle.com/#!6/0f9d9/7
I did the following:
DECLARE #TableB TABLE (id INT)
-- INSERT INTO #TableB
-- VALUES (some ids to filter by)
SELECT TOP 10 *
FROM [TableA] A
LEFT JOIN #TableB B
ON A.ID = B.id
WHERE B.id IS NOT NULL
OR iif(exists(SELECT *
FROM TableB), 1, 0) = 0
Now:
If TableB is empty (leave the commented lines commented) you'll get the top 10.
If TableB has some ids in it, you'll only join by those.
I do not know how efficient this is. Comments are welcome.
Maybe use a CTE
;WITH ctetable(
Select * from TableA
)
IF(EXISTS(SELECT 1 FROM TableB))
BEGIN
Select * from ctetable
Inner join TableB
END
ELSE
BEGIN
Select * from ctetable
END
or dynamic SQL
DECLARE #Query NVARCHAR(max);
SET #QUERY = 'Select * FROM TableA';
IF(EXISTS(SELECT 1 FROM TableB))
BEGIN
SET #QUERY = CONCAT(#QUERY,' INNER JOIN TableB');
END
EXEC sp_executesql #Query

SQL: Join tables on substrings

I have a table A with the string-column a and a table B with the string-column b. a is a substring of b. Now I want to join the the two tables on a and b. Is this possible?
I want something like this:
Select * from A,B where A.a *"is substring of"* B.b
How can I write this in SQL (Transact-SQL)?
You can use like
select *
from A
inner join B
on B.b like '%'+A.a+'%'
declare #tmp1 table (id int, a varchar(max))
declare #tmp2 table (id int, b varchar(max))
insert into #tmp1 (id, a) values (1,'one')
insert into #tmp2 (id,b) values (1,'onetwo')
select * from #tmp1 one inner join #tmp2 two on charindex(one.a,two.b) > 0
You can also use charindex, 0 means its not found, greater than 0 is the start index
charindex
set an inner join on a substring(4 letters) of FIELD1 of table TABLE1 with FIELD1 of table TABLE2
select TABLE1.field1,TABLE2.field1 from TABLE1 inner join TABLE2 on substring(TABLE1.field1,2,5)=TABLE2.field1
You have the contains function:
http://msdn.microsoft.com/en-us/library/ms187787.aspx
select * from A,B where contains(B.b, A.a)
try this:
Select * from A,B where B.b LIKE '%'+A.a+'%'
Select * from A
Join B on B.b = substr(A.a, 1, 5)
The number 1 represents the first character of your string but you would pick the begin and the end of the characters you would like to match of course. Could be 5, 9 too for example.