Be careful when using T-Sql IN operator [closed] - sql-server

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 5 years ago.
Improve this question
Is there anyone to explain that why this kind of queries running like where id_t1=id_t1 because of IN operator?
My colleague deleted whole table rows even though he wanted to delete some rows, when we analyzed query we realized that there is a problem using "IN" operator. Query structure was not proper but it ran.
I want to show this stuation how to become:
create table #table1(id_t1 int)
create table #table2(id_t2 int)
--insert data
insert into #table1(id_t1) values(1)
insert into #table1(id_t1) values(2)
insert into #table1(id_t1) values(3)
insert into #table1(id_t1) values(4)
insert into #table2(id_t2) values(10)
insert into #table2(id_t2) values(20)
insert into #table2(id_t2) values(30)
insert into #table2(id_t2) values(40)
as you can see there is no id_t1 in #table2,
when you run
select id_t1 from #table2 where id_t2 = 10
query returns:
Invalid column name 'id_t1'.
but it can be using with "in" operator
select * from #table1 where id_t1 in (select id_t1 from #table2 where id_t2 = 10)
it returns whole #table1 rows
id_t1
---------
1
2
3
4
when you use for delete operation like above
delete from #table1 where id_t1 in (select id_t1 from #table2 where id_t2 = 10)
your #table1 have no more rows :)
select * from #table1
id_t1
-----------
(0 row(s)
drop table #table1
drop table #table2
whole of my sample script to easily copy/paste:
create table #table1(id_t1 int)
create table #table2(id_t2 int)
--insert data
insert into #table1(id_t1) values(1)
insert into #table1(id_t1) values(2)
insert into #table1(id_t1) values(3)
insert into #table1(id_t1) values(4)
insert into #table2(id_t2) values(10)
insert into #table2(id_t2) values(20)
insert into #table2(id_t2) values(30)
insert into #table2(id_t2) values(40)
select * from #table1 where id_t1 in (select id_t1 from #table2 where id_t2 = 10)
delete from #table1 where id_t1 in (select id_t1 from #table2 where id_t2 = 10)
select * from #table1
drop table #table1
drop table #table2

In your select query
select * from #table1 where id_t1 in (select id_t1 from #table2 where id_t2 = 10)
SQL interprets it as
select * from #table1 where #table1.id_t1 in (select #table1.id_t1 from #table2 where id_t2 = 10)
So, for each row in #table1, the in clause is the value of #table1.id_t1, repeated for each row of table 2. So of course, every row qualifies.
This is why you should never run a delete statement without checking the select first...

that is because id_t1 does exists in table1 and in your where you did not say that id_t1 has to come from the correct table.
Using an alias for every table can solve this problem very easy.
This would be better
select *
from #table1 t1
where t1.id_t1 in (select t2.id_t1 from #table2 t2 where t2.id_t2 = 10)
now you get Invalid column name id_t1 like you expected

Sub query
select id_t1 from #table2 where id_t2 = 10
will return column Id_t1 of #Table1 corresponding, so condition IN always matched.
The correct query should be
select *
from #table1 where id_t1 in
(select t2.id_t2 from #table2 t2 where t2.id_t2 = 10)
Or using EXIST
select * from #table1 t1
where Exist (select t2.id_t2 from #table2 t2 where t2.id_t2 = 10 and t2.id_t2 = t1.id_t1)
Or using INNER JOIN
select *
from #table1 t1
inner join #table2 t2 on t1.id_t1 = t2.id_t2
where t2.id_t2 = 10
Logic is same when delete

Related

SQL Server exists not working in select statement

Query 1 below returns the correct result. But why does query2 return a wrong result? It looks like the exists in query2 doesn't work as expected. Tested on SQL Server 2016.
create table #t1 (id nvarchar(20), typ nvarchar(20))
insert into #t1
values ('1', 'stu'), ('2', 'exstu'), ('string', null)
create table #t2 (id int)
insert into #t2
values (1), (3)
--
--select * from #t1
--select * from #t2
--drop table #t1
--drop table #t2
-- Query #1:
select *
from #t1
where #t1.typ = 'stu' or typ = 'exstu'
and exists (select * from #t2
where #t1.id = #t2.id)
-- Query #2:
select *
from #t1
where exists (select * from #t2
where #t1.id = #t2.id)
and #t1.typ = 'stu' or typ = 'exstu'
AND takes precedence hence the last condition typ = 'exstu' in your second query causing and additional rows to be included in result set. Please check operator-precedence-transact-sql
Assuming you want both queries to give the same result, you'll need to add parentheses to your second query's WHERE clause to have the boolean logic match up:
select *
from #t1
where
exists (
select * from #t2
where #t1.id = #t2.id
)
and (#t1.typ = 'stu' or typ = 'exstu')

Create a view in sql server using dynamic sql inside

I know this sounds weird, but is it possible to have a view that use dynamic SQL to build it? I know the views are compiled so most probably this is not possible. For sure I could do it using an stored procedure instead but I just want to make sure is not possible.
Here I have an example:
declare #Table1 as table (
Id int,
Name nvarchar(50),
Provider nvarchar(50)
)
insert #Table1 values (1, 'John', 'Provider1')
insert #Table1 values (2, 'Peter', 'Provider1')
insert #Table1 values (3, 'Marcus', 'Provider2')
declare #Table2 as table (
Id int,
Info nvarchar(50),
AnotherInfo nvarchar(50)
)
insert #Table2 values (1, 'Expense', '480140')
insert #Table2 values (1, 'Maintenance', '480130')
insert #Table2 values (2, 'Set Up Cost', '480150')
insert #Table2 values (2, 'Something', '480160')
--No columns from Table2
select a.Id, a.Name, a.Provider from #Table1 a left join #Table2 b on a.Id = b.Id
--With columns from Table2
select a.Id, a.Name, a.Provider, b.Info, b.AnotherInfo from #Table1 a left join #Table2 b on a.Id = b.Id
The first select looks like I have repeated data, which is normal because I did the left join, the problem is that for avoiding that I need to perform a distinct and this is what I don't want to do. My example is short but I have much more columns and table is quite big.

How to autoincrement the id without identity?

I'm trying do to a bulk insert from another table in sql server. My query is currently like that :
INSERT INTO Table1(Id, Value)
SELECT ??, Value
FROM Table2;
Now, my problem is obviously by what I replace ??. Id is an integer column without an identity property. I would like that for each inserted row, Id take the current max(Id) + 1.
Can I do that directly in my insert command?
If you were using a newer version of SQL Server (2008+) you could try ROW_NUMBER():
DECLARE #BASE INT
SET #BASE = (SELECT IsNull(MAX(ID),0) FROM Table1)
INSERT INTO Table1(Id, Value)
SELECT
#BASE + ROW_NUMBER() OVER (ORDER BY Value) ID,
Value
FROM Table2;
SQL Fiddle
Since you are using SQL Server 2000, you could try like bellow:
DECLARE #BASE INT
SET #BASE = (SELECT IsNull(MAX(ID),0) FROM Table1)
INSERT INTO Table1(Id, Value)
SELECT
#BASE + (SELECT COUNT(*) FROM Table2 AS i2 WHERE i2.Value <= a.Value),
a.Value
FROM Table2 a
But it will only works if Value in Table2 is unique
SQL Fiddle
If Table2 has a primary key (field PK), then you could use:
INSERT INTO Table1(Id, Value)
SELECT
#BASE + (SELECT COUNT(*) FROM Table2 AS i2 WHERE i2.PK <= a.PK),
a.Value
FROM Table2 a
Here is one wicked way.
We create a temp table with identity to generate new ids. This way we avoid the while loop.
DECLARE #CurrentMaxID INT,
#DynamicQuery NVARCHAR(MAX)
--TODO : Acquired table lock here on table1
SELECT #FirstNextID = ISNULL(MAX(Id), 0)
FROM Table1 --WITH(TABLOCK)
CREATE TABLE #TempTableWithID( Table2Id INT,
Table1FuturId INT IDENTITY(1, 1))
INSERT INTO #TempTableWithID(Table2Id)
SELECT Id --Here we use identity to generate table1 futur id
FROM Table2
INSERT INTO Table1(Id, value)
SELECT Temp.Table1FuturId + #FirstNextID,
Table2.Value
FROM Table2
INNER JOIN #TempTableWithID AS Temp ON Table2.Id = Temp.Table2Id
--TODO : release table lock here on table1
DROP TABLE #TempTableWithID
If I'm understanding you correctly, this should work.
CREATE TABLE #tbl1 (ID int, Value float)
CREATE TABLE #tbl2 (ID int, Value float)
INSERT INTO #tbl2 values (4, 2.0)
INSERT INTO #tbl2 values (8, 3.0)
INSERT INTO #tbl2 values (6, 4.0)
INSERT INTO #tbl1 values (1,1.0)
INSERT INTO #tbl1 values (3,3)
INSERT INTO #tbl1 values (9,3)
/*meat and potatoes start*/
INSERT INTO #tbl1(Id, Value)
SELECT (SELECT MAX(ID) FROM #tbl1) + ROW_NUMBER() OVER (ORDER BY Value) ID, Value
FROM #tbl2;
/*meat and potatoes end*/
Select * From #tbl1
drop table #tbl1
drop table #tbl2
Why not IDENT_CURRENT() ?
SELECT IDENT_CURRENT('yourtablename')
It gives you the next ID reference. But this only works if the ID column has IDENTITY turned on.
OR you can try a SEQUENCE and the NEXT VALUE FOR.
i.e.
CREATE TABLE Test.TestTable
(CounterColumn int PRIMARY KEY,
Name nvarchar(25) NOT NULL) ;
GO
INSERT Test.TestTable (CounterColumn,Name)
VALUES (NEXT VALUE FOR Test.CountBy1, 'Syed') ;
GO
SELECT * FROM Test.TestTable;
GO

how to remove duplicate rows from a table in SQL Server [duplicate]

This question already has answers here:
How can I remove duplicate rows?
(43 answers)
Closed 9 years ago.
I have a table called table1 which has duplicate values. It looks like this:
new
pen
book
pen
like
book
book
pen
but I want to remove the duplicated rows from that table and insert them into another table called table2.
table2 should look like this:
new
pen
book
like
How can I do this in SQL Server?
Let's assume the field was named name:
INSERT INTO table2 (name)
SELECT name FROM table1 GROUP BY name
that query would get you all the unique names.
You could even put them into a table variable if you wanted:
DECLARE #Table2 TABLE (name VARCHAR(50))
INSERT INTO #Table2 (name)
SELECT name FROM table1 GROUP BY name
or you could use a temp table:
CREATE TABLE #Table2 (name VARCHAR(50))
INSERT INTO #Table2 (name)
SELECT name FROM table1 GROUP BY name
You can do this easily with a INSERT that SELECTs from a CTE where you use ROW_NUMBER(), like:
DECLARE #YourTable table (YourColumn varchar(10))
DECLARE #YourTable2 table (YourColumn varchar(10))
INSERT INTO #YourTable VALUES ('new')
INSERT INTO #YourTable VALUES ('pen')
INSERT INTO #YourTable VALUES ('book')
INSERT INTO #YourTable VALUES ('pen')
INSERT INTO #YourTable VALUES ('like')
INSERT INTO #YourTable VALUES ('book')
INSERT INTO #YourTable VALUES ('book')
INSERT INTO #YourTable VALUES ('pen')
;WITH OrderedResults AS
(
SELECT
YourColumn, ROW_NUMBER() OVER (PARTITION BY YourColumn ORDER BY YourColumn) AS RowNumber
FROM #YourTable
)
INSERT INTO #YourTable2
(YourColumn)
SELECT YourColumn FROM OrderedResults
WHERE RowNumber=1
SELECT * FROM #YourTable2
OUTPUT:
YourColumn
----------
book
like
new
pen
(4 row(s) affected)
You can do this easily with a DELETE on a CTE where you use ROW_NUMBER(), like:
--this will just remove them from your original table
DECLARE #YourTable table (YourColumn varchar(10))
INSERT INTO #YourTable VALUES ('new')
INSERT INTO #YourTable VALUES ('pen')
INSERT INTO #YourTable VALUES ('book')
INSERT INTO #YourTable VALUES ('pen')
INSERT INTO #YourTable VALUES ('like')
INSERT INTO #YourTable VALUES ('book')
INSERT INTO #YourTable VALUES ('book')
INSERT INTO #YourTable VALUES ('pen')
;WITH OrderedResults AS
(
SELECT
YourColumn, ROW_NUMBER() OVER (PARTITION BY YourColumn ORDER BY YourColumn) AS RowNumber
FROM #YourTable
)
DELETE OrderedResults
WHERE RowNumber!=1
SELECT * FROM #YourTable
OUTPUT:
YourColumn
----------
new
pen
book
like
(4 row(s) affected)
I posted something on deleting duplicates a couple of weeks ago by using DELETE TOP X. Only for a single set of duplicates obviously. However in the comments I was given this little jewel by Joshua Patchak.
;WITH cte(rowNumber) AS
(SELECT ROW_NUMBER() OVER (PARTITION BY [List of Natural Key Fields]
ORDER BY [List of Order By Fields])
FROM dbo.TableName)
DELETE FROM cte WHERE rowNumber>1
This will get rid of all of the duplicates in the table.
Here is the original post if you want to read the discussion. Duplicate rows in a table.

Deleting 'equivalent' data from two tables

I need to perform the following pseudo logic in a SQL Server 2012 procedure, based around a table variable and a table declared as such:
DECLARE #tmp TABLE
(
ID int IDENTITY(1,1),
UserID int NOT NULL,
SgsID int NOT NULL
)
CREATE TABLE #Table1
(
ID int IDENTITY(1,1),
UserID int NOT NULL,
SgsID int NOT NULL
)
For each row of data in table variable #tmp
Delete rows from Table1 where UserID/SgsID combinations match UserID/SgsID in Table1
Delete those UserID/SgsID combinations from #tmp that have been deleted from Table1
I've been researching different approaches, such as using OUTPUT INTO and INTERSECT, but cannot write a query that deletes across two tables (in fact I don't think it is even possible).
I have achieved the above steps by using the following code, however, I was wondering if any T-SQL pro's may be able to suggest a more succinct/efficient approach?
See SQLFiddle for online version
CREATE TABLE #Table1
(
ID int IDENTITY(1,1),
UserID int NOT NULL,
SgsID int NOT NULL
)
INSERT INTO #Table1 (UserID, SgsID) VALUES (5, 99)
INSERT INTO #Table1 (UserID, SgsID) VALUES (10, 89)
INSERT INTO #Table1 (UserID, SgsID) VALUES (150, 79)
INSERT INTO #Table1 (UserID, SgsID) VALUES (200, 69)
INSERT INTO #Table1 (UserID, SgsID) VALUES (250, 59)
SELECT * FROM #Table1
DECLARE #tmp TABLE
(
ID int IDENTITY(1,1),
UserID int NOT NULL,
SgsID int NOT NULL
)
INSERT INTO #tmp (UserID, SgsID) VALUES (150, 79)
INSERT INTO #tmp (UserID, SgsID) VALUES (200, 69)
INSERT INTO #tmp (UserID, SgsID) VALUES (250, 59)
INSERT INTO #tmp (UserID, SgsID) VALUES (999, 49)
SELECT * FROM #tmp
DECLARE #tbl_commonRows TABLE (UserID int, SgsID int)
INSERT INTO #tbl_commonRows
(
UserID,
SgsID
)
SELECT
UserID,
SgsID
FROM
#Table1
INTERSECT
SELECT
UserID,
SgsID
FROM
#tmp
DELETE FROM
#Table1
WHERE
(ID IN (
SELECT
ID
FROM
#Table1 t1 INNER JOIN
#tbl_commonRows c ON c.UserID = t1.UserID AND c.SgsID = t1.SgsID))
DELETE FROM
#tmp
WHERE
(ID IN (
SELECT
ID
FROM
#tmp t2 INNER JOIN
#tbl_commonrows c ON c.UserID = t2.UserID AND c.SgsID = t2.SgsID))
SELECT * FROM #Table1
SELECT * FROM #tmp
DROP TABLE #Table1
Here's solution:
DECLARE #tmp_ids TABLE (
id1 INT,
id2 INT
)
INSERT INTO #tmp_ids (id1, id2)
SELECT
t1.id,
t2.id
FROM Table1 t1
INNER JOIN tmp t2
on (t1.UserID = t2.UserID AND t1.SgsID = t2.SgsID)
DELETE FROM Table1
WHERE id IN (SELECT id1 FROM #tmp_ids)
DELETE FROM tmp
WHERE id IN (SELECT id2 FROM #tmp_ids)
Keep in mind - i created physical tables tmp and Table1
You can take advantage of the fact that the OUTPUT command can take more than INSERTED and DELETED columns for deletes (but not inserts, sadly):
DECLARE #output TABLE (id int)
DELETE FROM tbl
OUTPUT tmp.ID INTO #output(id)
FROM #Table1 tbl
JOIN #tmp tmp
ON tbl.UserID = tmp.UserID
AND tbl.SgsID = tmp.SgsID
DELETE FROM tmp
FROM #tmp tmp
JOIN #Output outp ON tmp.id = outp.id
Have you looked into using MERGE for this? Might be another option, and the syntax is nice and easy to follow.
MERGE (Transact-SQL)

Resources