Query executes despite invalid IN clause [duplicate] - sql-server

This question already has answers here:
sql server 2008 management studio not checking the syntax of my query
(2 answers)
Closed 6 years ago.
I was purging some invalid information from a database and ran the following query
delete from table1 where
table1id in( select table1id from wrongTable )
and
table1id not in (select validColumn from table3)
And it deleted several thousand rows.
Then I executed just :
select table1id from wrongTable
and got the error message:
Msg 207, Level 16, State 1, Line 1
Invalid column name 'table1id'
I then realized my error. My IN clause should have been
select fktable1id from correctTable
Luckily, I had the second clause. Otherwise, I would have completely wiped out the table.
Am I missing something or is this a bug? Shouldn't the first query have failed to execute?
Here is script to reproduce:
USE [SynDoc_Demo]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[temp1]') AND type in (N'U'))
DROP TABLE [dbo].[temp1]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[rightTable]') AND type in (N'U'))
DROP TABLE [dbo].[rightTable]
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[wrongTable]') AND type in (N'U'))
DROP TABLE [dbo].[wrongTable]
GO
create table temp1 (itemid int, name varchar(25))
go
insert into temp1 values (1,'bob')
insert into temp1 values (2,'dave')
go
create table wrongTable (junk int)
go
insert into wrongTable values (10)
go
create table rightTable (fkitemid int)
go
insert into rightTable values (2)
go
select * from temp1
go
delete from temp1 where itemid in (select itemid from wrongTable)
go
select * from temp1
EDIT:
Edited sample query to clarify for anyone attempting to reproduce

Not an answer but too long winded for the short comments.
Like Giorgos I couldn't reproduce until you added your script.
If I change the delete to
SELECT * FROM TEMP1 WHERE ITEMID IN (SELECT ITEMID FROM WRONGTABLE)
it still returns the two rows. Suspecting some kind of assumption by the engine that because ITEMID exists in TEMP1, tried to qualify it further
the following fails.
SELECT * FROM TEMP1 WHERE ITEMID IN (SELECT A.ITEMID FROM WRONGTABLE AS A)
Still - it's a great spot and one to be wary of.

Related

Create new tables from old tables [duplicate]

This question already has answers here:
How to create table using select query in SQL Server?
(4 answers)
Closed 9 years ago.
I want to create a table from select query result in SQL Server, I tried
create table temp AS select.....
but I got an error
Incorrect syntax near the keyword 'AS'
Use following syntax to create new table from old table in SQL server 2008
Select * into new_table from old_table
use SELECT...INTO
The SELECT INTO statement creates a new table and populates it with
the result set of the SELECT statement. SELECT INTO can be used to
combine data from several tables or views into one table. It can also
be used to create a new table that contains data selected from a
linked server.
Example,
SELECT col1, col2 INTO #a -- <<== creates temporary table
FROM tablename
Inserting Rows by Using SELECT INTO
Standard Syntax,
SELECT col1, ....., col# -- <<== select as many columns as you want
INTO [New tableName]
FROM [Source Table Name]
Please be careful,
MSSQL: "SELECT * INTO NewTable FROM OldTable"
is not always the same as
MYSQL: "create table temp AS select.."
I think that there are occasions when this (in MSSQL)
does not guarantee that all the fields in the new table are of the same type as the old.
For example :
create table oldTable (field1 varchar(10), field2 integer, field3 float)
insert into oldTable (field1,field2,field3) values ('1', 1, 1)
select top 1 * into newTable from oldTable
does not always yield:
create table newTable (field1 varchar(10), field2 integer, field3 float)
but may be:
create table newTable (field1 varchar(10), field2 integer, field3 integer)
Please try:
SELECT * INTO NewTable FROM OldTable
Try using SELECT INTO....
SELECT ....
INTO TABLE_NAME(table you want to create)
FROM source_table
Select [Column Name] into [New Table] from [Source Table]

How do I force a drop temp table command to fully drop the table?

I'm working on a script, just experimenting and developing. A lot of what I do involves temp tables, where I will select ... into #SomeTable from ..., at least in the initial experimentation/early dev stage.
Then I'll look at the results, make some changes, and go again. But to make things a little easier on myself, I also have a drop table if exists #SomeTable, so I can just rerun the code. So far so good. But, if I add a column to the temp table and then try to access it, I get the error Invalid column name 'newColumn'. I can avoid the error by executing JUST the drop statement, and then executing the whole script, but I would rather not have to do that. As I understand it, there is some caching of temp tables that goes on, and I wonder if that's the culprit. In any case, is there any way to fix this?
Edit: Here is a short script demonstrating the problem. Run the script, then uncomment both comments and run it again:
drop table if exists #DemoTable
select column1 = '1'
,column2 = '2'
-- ,column3 = '3'
into #DemoTable
select column1
,column2
-- ,column3
from #DemoTable
In SSMS you can just add a GO statement to separate the code in 2 batches.
This way the drop is executed before the 2nd part of the script is checked for errors.
drop table if exists #DemoTable
GO
select column1 = '1'
,column2 = '2'
-- ,column3 = '3'
into #DemoTable
select column1
,column2
-- ,column3
from #DemoTable
I have tried to follow the steps mentioned in the question.
Please find the following query samples using Northwind Database.
Added a new column to the temp table and also updated it sucessfully.
-- Create the temporary table #temp_Employees from a physical table called 'Employee' in schema 'dbo' in database 'Northwind'
SELECT EmployeeID, FirstName, LastName , BirthDate
INTO #temp_Employees
FROM [Northwind].[dbo].[Employees]
-- SELECT data from temptable '[#temp_Employees]'
SELECT * FROM #temp_Employees;
-- Add a new column '[City]' to table '[#temp_Employees]'
ALTER TABLE #temp_Employees
ADD [City] NVARCHAR(15) NULL
GO
-- SELECT data from temptable '[#temp_Employees]'
SELECT * FROM #temp_Employees;
-- UPDATE the new column '[City]' in the table '[#temp_Employees]'
UPDATE T
SET T.[City]=E.[City]
FROM #temp_Employees T INNER JOIN [Northwind].[dbo].[Employees] E ON T.EmployeeID=E.EmployeeID;
-- SELECT data from temptable '[#temp_Employees]'
SELECT * FROM #temp_Employees;
-- Drop the temptable if it already exists
IF OBJECT_ID('tempDB..#temp_Employees', 'U') IS NOT NULL
DROP TABLE #temp_Employees;
GO
-- SELECT data from temptable '[#temp_Employees]'
SELECT * FROM #temp_Employees;
Thank you.

Copy data from one table into another new table where columns are unknown

I'm trying to copy all data from Table1 into Table2.
I don't know what and how many columns are their in table 1. I mean I want to copy even column names from table 1 to table 2.
There is option like
insert *
into #table2
from Table1
but I even can't use this because there are many select query which has already been written at past. So I have to do something like this.
insert *
into #table2
from (select * from Table1)
This is throwing an error
Incorrect syntax near )
Try This:
Select * into #table2 from (select * from table1 ) as X
---To copy along with data..
select * into newtable from oldtable
--to copy only schema..
select * into newtable from oldtable where 1=2

Temporary table in SQL Server - error occurs if the query takes long time to complete

Have a look a the following query.
select *
into TempTable
from MainTable
select * from TempTable
WAITFOR DELAY '00:00:10'
drop table TempTable
After executing this query, I open other window and execute the query:
select *
into TempTable
from MainTable
select * from TempTable
drop table TempTable
I get the following error:
There is already an object named 'TempBucket' in the database.
Suppose that it a part of the stored procedure, and it takes a long time to finish. If there's a second call to this stored procedure, this error will occur. How do I fix this error?
I assume you are using MSSQL DBMS by the tags against your post.
Use a genuine temp table: prefix the name of the table with '#'.
Using this method the temp table will exist only in the scope of the procedure within which it was created.
select *
into #TempTable
from MainTable
select * from #TempTable
No drop actually neccessary but is probably better practice.
Try this one -
IF OBJECT_ID (N'tempdb.dbo.#TempTable', 'U') IS NOT NULL
DROP TABLE #TempTable
SELECT *
INTO #TempTable
FROM dbo.MainTable
SELECT *
FROM #TempTable

What columns can be used in OUTPUT INTO clause?

I'm trying to build a mapping table to associate the IDs of new rows in a table with those that they're copied from. The OUTPUT INTO clause seems perfect for that, but it doesn't seem to behave according to the documentation.
My code:
DECLARE #Missing TABLE (SrcContentID INT PRIMARY KEY )
INSERT INTO #Missing
( SrcContentID )
SELECT cshadow.ContentID
FROM Private.Content AS cshadow
LEFT JOIN Private.Content AS cglobal ON cshadow.Tag = cglobal.Tag
WHERE cglobal.ContentID IS NULL
PRINT 'Adding new content headers'
DECLARE #Inserted TABLE (SrcContentID INT PRIMARY KEY, TgtContentID INT )
INSERT INTO Private.Content
( Tag, Description, ContentDate, DateActivate, DateDeactivate, SortOrder, CreatedOn, IsDeleted, ContentClassCode, ContentGroupID, OrgUnitID )
OUTPUT cglobal.ContentID, INSERTED.ContentID INTO #Inserted (SrcContentID, TgtContentID)
SELECT Tag, Description, ContentDate, DateActivate, DateDeactivate, SortOrder, CreatedOn, IsDeleted, ContentClassCode, ContentGroupID, NULL
FROM Private.Content AS cglobal
INNER JOIN #Missing AS m ON cglobal.ContentID = m.SrcContentID
Results in the error message:
Msg 207, Level 16, State 1, Line 34
Invalid column name 'SrcContentID'.
(line 34 being the one with the OUTPUT INTO)
Experimentation suggests that only rows that are actually present in the target of the INSERT can be selected in the OUTPUT INTO. But this contradicts the docs in the books online. The article on OUTPUT Clause has example E that describes a similar usage:
The OUTPUT INTO clause returns values
from the table being updated
(WorkOrder) and also from the Product
table. The Product table is used in
the FROM clause to specify the rows to
update.
Has anyone worked with this feature?
(In the meantime I've rewritten my code to do the job using a cursor loop, but that's ugly and I'm still curious)
You can do this with a MERGE in Sql Server 2008. Example code below:
--drop table A
create table A (a int primary key identity(1, 1))
insert into A default values
insert into A default values
delete from A where a>=3
-- insert two values into A and get the new primary keys
MERGE a USING (SELECT a FROM A) AS B(a)
ON (1 = 0) -- ignore the values, NOT MATCHED will always be true
WHEN NOT MATCHED THEN INSERT DEFAULT VALUES -- always insert here for this example
OUTPUT $action, inserted.*, deleted.*, B.a; -- show the new primary key and source data
Result is
INSERT, 3, NULL, 1
INSERT, 4, NULL, 2
i.e. for each row the new primary key (3, 4) and the old one (1, 2). Creating a table called e.g. #OUTPUT and adding " INTO #OUTPUT;" at the end of the OUTPUT clause would save the records.
I've verified that the problem is that you can only use INSERTED columns. The documentation seems to indicate that you can use from_table_name, but I can't seem to get it to work (The multi-part identifier "m.ContentID" could not be bound.):
TRUNCATE TABLE main
SELECT *
FROM incoming
SELECT *
FROM main
DECLARE #Missing TABLE (ContentID INT PRIMARY KEY)
INSERT INTO #Missing(ContentID)
SELECT incoming.ContentID
FROM incoming
LEFT JOIN main
ON main.ContentID = incoming.ContentID
WHERE main.ContentID IS NULL
SELECT *
FROM #Missing
DECLARE #Inserted TABLE (ContentID INT PRIMARY KEY, [Content] varchar(50))
INSERT INTO main(ContentID, [Content])
OUTPUT INSERTED.ContentID /* incoming doesn't work, m doesn't work */, INSERTED.[Content] INTO #Inserted (ContentID, [Content])
SELECT incoming.ContentID, incoming.[Content]
FROM incoming
INNER JOIN #Missing AS m
ON m.ContentID = incoming.ContentID
SELECT *
FROM #Inserted
SELECT *
FROM incoming
SELECT *
FROM main
Apparently the from_table_name prefix is only allowed on DELETE or UPDATE (or MERGE in 2008) - I'm not sure why:
from_table_name
Is a column prefix that specifies a table included in the FROM clause of a DELETE or UPDATE statement that is used to specify the rows to update or delete.
If the table being modified is also specified in the FROM clause, any reference to columns in that table must be qualified with the INSERTED or DELETED prefix.
I'm running into EXACTLY the same problem as you are, I feel your pain...
As far as I've been able to find out there's no way to use the from_table_name prefix with an INSERT statement.
I'm sure there's a viable technical reason for this, and I'd love to know exactly what it is.
Ok, found it, here's a forum post on why it doesn't work:
MSDN forums
I think I found a solution to this problem, it sadly involves a temporary table, but at least it'll prevent the creation of a dreaded cursor :)
What you need to do is add an extra column to the table you're duplicating records from and give it a 'uniqueidentifer' type.
then declare a temporary table:
DECLARE #tmptable TABLE (uniqueid uniqueidentifier, original_id int, new_id int)
insert the the data into your temp table like this:
insert into #tmptable
(uniqueid,original_id,new_id)
select NewId(),id,0 from OriginalTable
the go ahead and do the real insert into the original table:
insert into OriginalTable
(uniqueid)
select uniqueid from #tmptable
Now to add the newly created identity values to your temp table:
update #tmptable
set new_id = o.id
from OriginalTable o inner join #tmptable tmp on tmp.uniqueid = o.uniqueid
Now you have a lookup table that holds the new id and original id in one record, for your using pleasure :)
I hope this helps somebody...
(MS) If the table being modified is also specified in the FROM clause, any reference to columns in that table must be qualified with the INSERTED or DELETED prefix.
In your example, you can't use cglobal table in the OUTPUT unless it's INSERTED.column_name or DELETED.column_name:
INSERT INTO Private.Content
(Tag)
OUTPUT cglobal.ContentID, INSERTED.ContentID
INTO #Inserted (SrcContentID, TgtContentID)
SELECT Tag
FROM Private.Content AS cglobal
INNER JOIN #Missing AS m ON cglobal.ContentID = m.SrcContentID
What worked for me was a simple alias table, like this:
INSERT INTO con1
(Tag)
OUTPUT **con2**.ContentID, INSERTED.ContentID
INTO #Inserted (SrcContentID, TgtContentID)
SELECT Tag
FROM Private.Content con1
**INNER JOIN Private.Content con2 ON con1.id=con2.id**
INNER JOIN #Missing AS m ON con1.ContentID = m.SrcContentID

Resources