How to create a table in sql server like other table? - sql-server

I want to create a table like another table in SQL Server.
In Postgres I used to create it with this request:
CREATE TABLE IF NOT EXISTS table2 (LIKE table1 INCLUDING ALL) INHERITS (table1)

Something like this should work:
IF OBJECT_ID('table1', 'U') IS NULL BEGIN
SELECT TOP 0 * INTO table1 FROM table2
END
This will clone the column definition of table2 in table1 without inserting any data (because of the top 0).
Then you have to create the PK, UK, FK for the table1. For this purpose you can check: https://www.mssqltips.com/sqlservertip/3443/script-all-primary-keys-unique-constraints-and-foreign-keys-in-a-sql-server-database-using-tsql/
With this resource you can generate an ALTER TABLE script to add all PK, UK, FK in a variable and run it like:
DECLARE #sqlCommand nvarchar(1000)
EXECUTE sp_executesql #sqlCommand

SELECT *
INTO table2
FROM table1 where 1=2
SELECT * = select all columns
INTO= place of set
FROM = place to lookup,
WHERE 1=2 = filter criteria

select top 0 * into x from y
will give you an empty shell of a table with no data exactly defined as the original table minus all the indexes , constraints etc

Related

SQL Server Query to get data from multiple tables

I need a sql query to get records on the basis of multiple tables. Like below -
There are tables Table_A, Table_B & Table_C. In which Table_A is the main table in which I want to get the data. And Table_B is dependent on Table_A, containing the Foreign Key of Table_A.
Table_A (parent table) ----> Table_B(child table)
And Table_C is contains the Foreign key of table Table_B.
Table_B (parent table) ----> Table_C(child table)
So now I need a data from Table_A and one of the column of Table_C. But that column will contain the comma separate string on the basis of Table_A's primary key value.
So now I need a data from Table_A and one of the column of Table_C.
But that column will contain the comma separate string on the basis of
Table_A's primary key value.
This is the wrong way to solve your abstract problem. If you want multiple items in Table C to be associated with Table A (and based on the comment above Table B has no effect) then you should do the following:
Create a table called CjoinA which has an ID_C and an ID_A a column Then there will be multiple rows for the relations ship so if Table C row ID 1 is related to rows of Table A with IDs 1, 2 and 3 then you would have three rows like this
ID_C ID_A
1 1
1 2
1 3
A possible way would be to create a function which concats the values of table_C:
CREATE FUNCTION [dbo].[GET_TBC](#parentID Integer)
returns nvarchar(MAX)
Begin
declare #result as varchar(MAX)
set #result = ''
select #result = #result + _YOURCOLUMN_ + '; '
from TABLE_C
WHERE PARENT_ID_COL = #parentID
select #result = left (#result , len(#result )-1);
return #result
end
and use this function in your select statement:
SELECT TableA.*, GET_TBC(TableB.ID)
FROM TableA, TableB
WHERE TableB.ParentID = TableA.ID

Create new table from Existing table With out identity property to be copied

I am using select * into to create a table from another table and used SET IDENTITY_INSERT ON for newly created table to accept data including identity column.
Now my question is that Is there any way to create a table (say TABLE A) from another existing table (say TABLE B) with out copying identity property of TABLE B to TABLE A.
Of course, let's say TABLEA looks like this:
CREATE TABLE TABLEA (
ID INT PRIMARY KEY IDENTITY(1, 1),
Column1 VARCHAR(50)
)
and let's assume that TABLEB looks the same. If we wanted to move data from TABLEB into TABLEA without moving the identity column we would write this statement:
INSERT INTO TABLEA (Column1)
SELECT Column1 FROM TABLEB
and this will cause every new row in TABLEA to get its own identity value.
But note, you can't issue a SELECT * FROM TABLEB because that would also select the ID column. You have to list out all of the fields you want individually. However, generally speaking, I think a lot of very experienced DBA's and programmers would say that SELECT * FROM Table is really dangerous because if the schema changes everything breaks. You really want to always be specific.
SELECT STUFF((SELECT ', ' + c.name FROM sys.columns c where c.object_id = OBJECT_ID('dbo.TABLE_NAME') and c.is_identity <> 1 FOR XML PATH('')),1, 2, '') AS CSVColumn
This will give you a comma separated list of columns of each table. Copy these statements into notepad to create a script that you can run.

Create Table Variable (or Temp Table) from an existing Table but with an Extra Column

I want to create a table variable or temporary table, using data from another table but with an extra column.
I know I could use SELECT INTO as follows
SELECT *
INTO #TempTable
FROM Existing_Table
WHERE Section = 2
and then use
ALTER TABLE #TempTable ADD New_Column
And finally
DROP #TempTable
I will then be looping through each row with a dynamically called stored procedure to place values in the new column. Its not a big table.
Is that the best way? My understanding is that I cannot use the above with a table variable.
Thanks
Chris.
Its simpler
SELECT *, cast('hbkkj' as nvarchar(100)) as New_Column
INTO #TempTable
FROM Existing_Table
WHERE Section = 2

An explicit value for the identity column in table can only be specified when a column list is used and IDENTITY_INSERT is ON SQL Server

I'm trying to do this query
INSERT INTO dbo.tbl_A_archive
SELECT *
FROM SERVER0031.DB.dbo.tbl_A
but even after I ran
set identity_insert dbo.tbl_A_archive on
I am getting this error message
An explicit value for the identity column in table 'dbo.tbl_A_archive' can only be specified when a column list is used and IDENTITY_INSERT is ON.
tbl_A is a huge table in rows and width, i.e. it has a LOT of columns. I do not want to have to type all the columns out manually. How can I get this to work?
SET IDENTITY_INSERT tableA ON
You have to make a column list for your INSERT statement:
INSERT Into tableA ([id], [c2], [c3], [c4], [c5] )
SELECT [id], [c2], [c3], [c4], [c5] FROM tableB
not like "INSERT Into tableA SELECT ........"
SET IDENTITY_INSERT tableA OFF
Summary
SQL Server won't let you insert an explicit value in an identity column unless you use a column list. Thus, you have the following options:
Make a column list (either manually or using tools, see below)
OR
make the identity column in tbl_A_archive a regular, non-identity column: If your table is an archive table and you always specify an explicit value for the identity column, why do you even need an identity column? Just use a regular int instead.
Details on Solution 1
Instead of
SET IDENTITY_INSERT archive_table ON;
INSERT INTO archive_table
SELECT *
FROM source_table;
SET IDENTITY_INSERT archive_table OFF;
you need to write
SET IDENTITY_INSERT archive_table ON;
INSERT INTO archive_table (field1, field2, ...)
SELECT field1, field2, ...
FROM source_table;
SET IDENTITY_INSERT archive_table OFF;
with field1, field2, ... containing the names of all columns in your tables. If you want to auto-generate that list of columns, have a look at Dave's answer or Andomar's answer.
Details on Solution 2
Unfortunately, it is not possible to just "change the type" of an identity int column to a non-identity int column. Basically, you have the following options:
If the archive table does not contain data yet, drop the column and add a new one without identity.
OR
Use SQL Server Management Studio to set the Identity Specification/(Is Identity) property of the identity column in your archive table to No. Behind the scenes, this will create a script to re-create the table and copy existing data, so, to do that, you will also need to unset Tools/Options/Designers/Table and Database Designers/Prevent saving changes that require table re-creation.
OR
Use one of the workarounds described in this answer: Remove Identity from a column in a table
If you're using SQL Server Management Studio, you don't have to type the column list yourself - just right-click the table in Object Explorer and choose Script Table as -> SELECT to -> New Query Editor Window.
If you aren't, then a query similar to this should help as a starting point:
SELECT SUBSTRING(
(SELECT ', ' + QUOTENAME(COLUMN_NAME)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'tbl_A'
ORDER BY ORDINAL_POSITION
FOR XML path('')),
3,
200000);
Agree with Heinzi's answer. For first second option, here's a query that generates a comma-separated list of columns in a table:
select name + ', ' as [text()]
from sys.columns
where object_id = object_id('YourTable')
for xml path('')
For big tables, this can save a lot of typing work :)
If the "archive" table is meant to be an exact copy of you main table then I would just suggest that you remove the fact that the id is an identiy column. That way it will let you insert them.
Alternatively you can allow and the disallow identity inserts for the table with the following statement
SET IDENTITY_INSERT tbl_A_archive ON
--Your inserts here
SET IDENTITY_INSERT tbl_A_archive OFF
Finally, if you need the identity column to work as is then you can always just run the stored proc.
sp_columns tbl_A_archive
This will return you all of the columns from the table which you can then cut and paste into your query. (This is almost ALWAYS better than using a *)
For the SQL statement, you also have to specify the column list. For eg.
INSERT INTO tbl (idcol1,col2) VALUES ( value1,value2)
instead of
INSERT INTO tbl VALUES ( value1,value2)
Both will work but if you still get error by using #1 then go for #2
1)
SET IDENTITY_INSERT customers ON
GO
insert into dbo.tbl_A_archive(id, ...)
SELECT Id, ...
FROM SERVER0031.DB.dbo.tbl_A
2)
SET IDENTITY_INSERT customers ON
GO
insert into dbo.tbl_A_archive(id, ...)
VALUES(#Id,....)
SET IDENTITY_INSERT tableA ON
INSERT Into tableA ([id], [c2], [c3], [c4], [c5] )
SELECT [id], [c2], [c3], [c4], [c5] FROM tableB
Not like this
INSERT INTO tableA
SELECT * FROM tableB
SET IDENTITY_INSERT tableA OFF
In order to populate all of the column names into a comma-delimited list for a Select statement for the solutions mentioned for this question, I use the following options as they are a little less verbose than most responses here. Although, most responses here are still perfectly acceptable, however.
1)
SELECT column_name + ','
FROM information_schema.columns
WHERE table_name = 'YourTable'
2) This is probably the simplest approach to creating columns,
if you have SQL Server SSMS.
1) Go to the table in Object Explorer and click on the + to the left of the table name or double-click the table name to open the sub list.
2) Drag the column subfolder over to the main query area and it will autopaste the entire column list for you.
You must need to specify columns name which you want to insert if there is an Identity column.
So the command will be like this below:
SET IDENTITY_INSERT DuplicateTable ON
INSERT Into DuplicateTable ([IdentityColumn], [Column2], [Column3], [Column4] )
SELECT [IdentityColumn], [Column2], [Column3], [Column4] FROM MainTable
SET IDENTITY_INSERT DuplicateTable OFF
If your table has many columns then get those columns name by using this command.
SELECT column_name + ','
FROM information_schema.columns
WHERE table_name = 'TableName'
for xml path('')
(after removing the last comma(',')) Just copy past columns name.
This should work. I just ran into your issue:
SET IDENTITY_INSERT dbo.tbl_A_archive ON;
INSERT INTO dbo.tbl_A_archive (IdColumn,OtherColumn1,OtherColumn2,...)
SELECT *
FROM SERVER0031.DB.dbo.tbl_A;
SET IDENTITY_INSERT dbo.tbl_A_archive OFF;
Unfortunately it seems you do need a list of the columns including the identity column to insert records which specify the Identity. However, you don't HAVE to list the columns in the SELECT.
As #Dave Cluderay suggested this will result in a formatted list for you to copy and paste (if less than 200000 characters).
I added the USE since I'm switching between instances.
USE PES
SELECT SUBSTRING(
(SELECT ', ' + QUOTENAME(COLUMN_NAME)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Provider'
ORDER BY ORDINAL_POSITION
FOR XML path('')),
3,
200000);
This code snippet shows how to insert into table when identity Primary Key column is ON.
SET IDENTITY_INSERT [dbo].[Roles] ON
GO
insert into Roles (Id,Name) values(1,'Admin')
GO
insert into Roles (Id,Name) values(2,'User')
GO
SET IDENTITY_INSERT [dbo].[Roles] OFF
GO
Please make sure that the column names, data types, and order in the table from where you are selecting records is exactly same as the destination table. Only difference should be that destination table has an identity column as the first column, that is not there in source table.
I was facing similar issue when I was executing "INSERT INTO table_Dest SELECT * FROM table_source_linked_server_excel". The tables had 115 columns.
I had two such tables where I was loading data from Excel (As linked server) into tables in database. In database tables, I had added an identity column called 'id' that was not there in source Excel. For one table the query was running successfully and in another I got the error "An explicit value for the identity column in table can only be specified when a column list is used and IDENTITY_INSERT is ON SQL Server". This was puzzling as the scenario was exactly same for both the queries. So I investigated into this and what I found was that in the query where I was getting error with INSERT INTO .. SELECT *:
Some of the column names in source table were modified, though values were correct
There were some extra columns beyond actual data columns that were being selected by SELECT *. I discovered this by using the option of "Script table as > Select to > new query window" on the source Excel table (under linked servers). There was one hidden column just after the last column in Excel, though it did not have any data. I deleted that column in source Excel table and saved it.
After making the above two changes the query for INSERT INTO... SELECT * ran successfully. The identity column in destination table generated identity values for each inserted row as expected.
So, even though the destination table may have an identity column that is not there in source table, the INSERT INTO.. SELECT * will run successfully if the names, data types, and column order in source and destination are exactly the same.
Hope it helps someone.
For if you want to insert your values from one table to another through a stored procedure. I used this and this, the latter which is almost like Andomar's answer.
CREATE procedure [dbo].[RealTableMergeFromTemp]
with execute as owner
AS
BEGIN
BEGIN TRANSACTION RealTableDataMerge
SET XACT_ABORT ON
DECLARE #columnNameList nvarchar(MAX) =
STUFF((select ',' + a.name
from sys.all_columns a
join sys.tables t on a.object_id = t.object_id
where t.object_id = object_id('[dbo].[RealTable]')
order by a.column_id
for xml path ('')
),1,1,'')
DECLARE #SQLCMD nvarchar(MAX) =N'INSERT INTO [dbo].[RealTable] (' + #columnNameList + N') SELECT * FROM [#Temp]'
SET IDENTITY_INSERT [dbo].[RealTable] ON;
exec(#sqlcmd)
SET IDENTITY_INSERT [dbo].[RealTable] OFF
COMMIT TRANSACTION RealTableDataMerge
END
GO
I think this error occurs due to the mismatch with number of columns in table definition and number of columns in the insert query. Also the length of the column is omitted with the entered value.
So just review the table definition to resolve this issue
There is one or more column that has auto-increment property or the value of that attribute will be calculated as constraints. You are trying to modify that column.
There is two way to solve it
1) Mention other columns explicitly and set their values only and the PrimaryKey or the auto-increment column value will set automatically.
2) You can turn on INDENTITY_INSERT then execute your insert query finally turn off IDENTITY_INSERT.
Suggestion: Follow the first step because it is a more suitable and efficient approach.
For more information read this article on SQL-helper.
I use the following to create a temp exact as the table but without the identity:
SELECT TOP 0 CONVERT(INT,0)myid,* INTO #temp FROM originaltable
ALTER TABLE #temp DROP COLUMN id
EXEC tempdb.sys.sp_rename N'#temp.myid', N'id', N'COLUMN'
Gets a warning about renames but no big deal.
I use this on production class systems. Helps make sure the copy will follow any future table modifications and the temp produced is capable of getting rows additional times within a task. Please note that the PK constraint is also removed - if you need it you can add it at the end.
"an explicit value for the identity column in table can only be
specified when ..."
What fixed it for me, was not specifying the identity column.
So instead of
INSERT INTO dbo.tbl_A_archive
SELECT *
FROM SERVER0031.DB.dbo.tbl_A
I specified all columns but not the identity one (tbl_A_archive.ID)
INSERT INTO dbo.tbl_A_archive
SELECT col1, col2, colETC
FROM SERVER0031.DB.dbo.tbl_A

What's the best way to determine if a temporary table exists in SQL Server?

When writing a T-SQL script that I plan on re-running, often times I use temporary tables to store temporary data. Since the temp table is created on the fly, I'd like to be able to drop that table only if it exists (before I create it).
I'll post the method that I use, but I'd like to see if there is a better way.
IF Object_Id('TempDB..#TempTable') IS NOT NULL
BEGIN
DROP TABLE #TempTable
END
The OBJECT_ID function returns the internal object id for the given object name and type. 'tempdb..#t1' refers to the table #t1 in the tempdb database. 'U' is for user-defined table.
IF OBJECT_ID('tempdb..#t1', 'U') IS NOT NULL
DROP TABLE #t1
CREATE TABLE #t1
(
id INT IDENTITY(1,1),
msg VARCHAR(255)
)
SELECT name
FROM sysobjects
WHERE type = 'U' AND name = 'TempTable'

Resources