We are trying to track down record changes in a SQL Server table. Users are complaining they change a record in the morning and when they return in the afternoon, the value is changed back.
Example: column value was null. User changed to a value like 6. When they return to the record, the value might be null again or zero.
We turned on auditing in SQL Server, but I am not seeing the first edit (am) or the change back to null or zero. Yes, I am thinking user error but before I blame a user, I need to eliminate all other possibilities. I pulled a trigger event code from the web and modified it for my purposes. I put it on the specific table under triggers. It is pulling records as I expected. It keep track of what columns were changed, the old and the new values. I will admit I am not intimate with everything this code is doing so I want to ask if this code has any potential holes.
Example: would it not record an event if someone changed a column from 1 to NULL because of the NULL? ModifiedOn and ModifiedBy should be autofilled when the program saves a record. The trigger will capture the ModifiedOn but not consistently the ModifiedBy. A question was raised if ModifiedBy old value and new value matched (same person made both changes), would it not a record to the log the ModifiedBy because the values matched.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[TR_AppointmentBase_LOG]
ON [dbo].[AppointmentBase]
FOR UPDATE
AS
DECLARE #bit INT,
#field INT,
#maxfield INT,
#char INT,
#fieldname VARCHAR(128),
#TableName VARCHAR(128),
#PKCols VARCHAR(1000),
#sql VARCHAR(2000),
#UpdateDate VARCHAR(21),
#UserName VARCHAR(128),
#Type CHAR(1),
#PKSelect VARCHAR(1000),
#PKSelect2 VARCHAR(1000)
--You will need to change #TableName to match the table to be audited.
-- Here we made GUESTS for your example.
SELECT #TableName = 'AppointmentBase'
SELECT #UserName = SYSTEM_USER,
#UpdateDate = CONVERT(NVARCHAR(30), GETDATE(), 126)
-- Action
IF EXISTS (
SELECT *
FROM INSERTED
)
IF EXISTS (
SELECT *
FROM DELETED
)
SELECT #Type = 'U'
ELSE
SELECT #Type = 'I'
ELSE
SELECT #Type = 'D'
-- get list of columns
SELECT * INTO #ins
FROM INSERTED
SELECT * INTO #del
FROM DELETED
-- Get primary key columns for full outer join
SELECT #PKCols = COALESCE(#PKCols + ' and', ' on')
+ ' i.[' + c.COLUMN_NAME + '] = d.[' + c.COLUMN_NAME + ']'
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk,
INFORMATION_SCHEMA.KEY_COLUMN_USAGE c
WHERE pk.TABLE_NAME = #TableName
AND CONSTRAINT_TYPE = 'PRIMARY KEY'
AND c.TABLE_NAME = pk.TABLE_NAME
AND c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME
-- Get primary key select for insert
SELECT #PKSelect = COALESCE(#PKSelect + '+', '')
+ '''<[' + COLUMN_NAME
+ ']=''+convert(varchar(100),
coalesce(i.[' + COLUMN_NAME + '],d.[' + COLUMN_NAME + ']))+''>''',
#PKSelect2 = COALESCE(#PKSelect2 + '+', '')
+ '+convert(varchar(100), coalesce(i.[' + COLUMN_NAME + '],d.[' + COLUMN_NAME + ']))'
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk,
INFORMATION_SCHEMA.KEY_COLUMN_USAGE c
WHERE pk.TABLE_NAME = #TableName
AND CONSTRAINT_TYPE = 'PRIMARY KEY'
AND c.TABLE_NAME = pk.TABLE_NAME
AND c.CONSTRAINT_NAME = pk.CONSTRAINT_NAME
IF #PKCols IS NULL
BEGIN
RAISERROR('no PK on table %s', 16, -1, #TableName)
RETURN
END
SELECT #field = 0,
-- #maxfield = MAX(COLUMN_NAME)
#maxfield = -- FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #TableName
MAX(
COLUMNPROPERTY(
OBJECT_ID(TABLE_SCHEMA + '.' + #TableName),
COLUMN_NAME,
'ColumnID'
)
)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #TableName
WHILE #field < #maxfield
BEGIN
SELECT #field = MIN(
COLUMNPROPERTY(
OBJECT_ID(TABLE_SCHEMA + '.' + #TableName),
COLUMN_NAME,
'ColumnID'
)
)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #TableName
AND COLUMNPROPERTY(
OBJECT_ID(TABLE_SCHEMA + '.' + #TableName),
COLUMN_NAME,
'ColumnID'
) > #field
SELECT #bit = (#field - 1)% 8 + 1
SELECT #bit = POWER(2, #bit - 1)
SELECT #char = ((#field - 1) / 8) + 1
IF SUBSTRING(COLUMNS_UPDATED(), #char, 1) & #bit > 0
OR #Type IN ('I', 'D')
BEGIN
SELECT #fieldname = COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #TableName
AND COLUMNPROPERTY(
OBJECT_ID(TABLE_SCHEMA + '.' + #TableName),
COLUMN_NAME,
'ColumnID'
) = #field
SELECT #sql =
'
insert into AppointmentBase_Log2 ( Type,
TableName,
PK,
FieldName,
OldValue,
NewValue,
UpdateDate,
UserName)
select ''' + #Type + ''','''
+ #TableName + ''',' + #PKSelect2
+ ',''' + #fieldname + ''''
+ ',convert(varchar(1000),d.' + #fieldname + ')'
+ ',convert(varchar(1000),i.' + #fieldname + ')'
+ ',''' + #UpdateDate + ''''
+ ',''' + #UserName + ''''
+ ' from #ins i full outer join #del d'
+ #PKCols
+ ' where i.' + #fieldname + ' <> d.' + #fieldname
+ ' or (i.' + #fieldname + ' is null and d.'
+ #fieldname
+ ' is not null)'
+ ' or (i.' + #fieldname + ' is not null and d.'
+ #fieldname
+ ' is null)'
EXEC (#sql)
END
END
Users and managers are thinking I am not finding all the changes to the table with my code. Basically because I can not prove they changed the record in the first place let alone the second change. Based on my limited knowledge of the process above, I am not confident enough to say they are wrong.
Is there a transaction that could update a record in the table that this code could miss? Say a scheduled maintenance task that might not trigger the "U","D" or "I" types this uses. Or maybe a batch injection from an outside source?
Or, do you think I'm good with the above and should look at the user or GUI interface as not completing the transaction.
I'm trying to execute the result of this query.
SELECT 'UPDATE [' + TABLE_SCHEMA + '].[' + TABLE_NAME + '] ' + 'SET [' + COLUMN_NAME + '] = RTRIM(LTRIM(SUBSTRING ([' + COLUMN_NAME + '], 2 , LEN ([' + COLUMN_NAME + ']) - 2) )) WHERE LEFT([' + COLUMN_NAME + '], 1) = ' + '''"''' + ' AND RIGHT([' + COLUMN_NAME + '], 1) = ' + '''"'''
FROM INFORMATION_SCHEMA.COLUMNS
WHERE DATA_TYPE IN ('varchar', 'nvarchar')
ORDER BY TABLE_NAME, COLUMN_NAME
It return more one row. For example:
UPDATE [dbo].[ACCDB] SET [ACODI] = RTRIM(LTRIM(SUBSTRING ([ACODI], 2 , LEN ([ACODI]) - 2) )) WHERE LEFT([ACODI], 1) = '"' AND RIGHT([ACODI], 1) = '"'
UPDATE [dbo].[ANAGP] SET [CDIVA] = RTRIM(LTRIM(SUBSTRING ([CDIVA], 2 , LEN ([CDIVA]) - 2) )) WHERE LEFT([CDIVA], 1) = '"' AND RIGHT([CDIVA], 1) = '"'
Now, how I execute this row programmatically? Without I write every row in stored procedure?
Thanks, I wait answer.
What you're trying to do here is dynamic SQL; building your queries on the fly. There are a number of ways to do that. Since you're building multiple statements, you need to be able to execute them one at a time. McNets's answer suggests using a cursor, which is one common way to do what you're after. Another way is to break up your statements with a loop, then execute them one after the other.
The code below gets a list of table names first, defining how many statements we may end up with. Mostly, the ID column is what we care about here. Then there's a WHILE loop to run your query once for each table, with a check to make sure we need to execute the statement for that table.
DECLARE #SqlText NVARCHAR(2000) --Variable to hold your query
,#Counter INT --Counter to keep track of where we are in the loop
,#EndLoop INT; --Value to stop the loop
DECLARE #TblList TABLE (
[ID] INT IDENTITY(1,1)
,[Schema] NVARCHAR(128)
,[Table] NVARCHAR(128));
INSERT INTO #TblList ([Schema],[Table])
SELECT
TABLE_SCHEMA
,TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES;
SELECT #Counter = MIN([ID]), #EndLoop = MAX([ID])
FROM #TblList;
WHILE #Counter <= #EndLoop
BEGIN
IF EXISTS ( --If there are no character columns, skip the table
SELECT 1
FROM INFORMATION_SCHEMA.COLUMNS AS col JOIN #TblList AS tbl
ON tbl.[Schema] = col.TABLE_SCHEMA
AND tbl.[Table] = col.TABLE_NAME
AND tbl.[ID] = #Counter
AND col.DATA_TYPE IN ('varchar', 'nvarchar')
)
BEGIN
--Construct the query
SELECT #SqlText = CAST('UPDATE [' + col.TABLE_SCHEMA + '].[' + col.TABLE_NAME + '] ' + 'SET [' + col.COLUMN_NAME + '] = RTRIM(LTRIM(SUBSTRING ([' + col.COLUMN_NAME + '], 2 , LEN ([' + col.COLUMN_NAME + ']) - 2) )) WHERE LEFT([' + col.COLUMN_NAME + '], 1) = ' + '''"''' + ' AND RIGHT([' + col.COLUMN_NAME + '], 1) = ' + '''"''' AS NVARCHAR(2000))
FROM INFORMATION_SCHEMA.COLUMNS AS col
JOIN #TblList AS tbl
ON tbl.[Schema] = col.TABLE_SCHEMA
AND tbl.[Table] = col.TABLE_NAME
AND tbl.[ID] = #Counter
AND col.DATA_TYPE IN ('varchar', 'nvarchar');
--Run the query
EXEC sp_executesql #SQL;
END
--Increment the counter to move through the table list
SET #Counter = #Counter + 1;
END
I'd suggest to use a CURSOR to execute it line by line.
Have a look at one o my answers on dba.stackexchange.com for a similar question.
Restore all IDENTITY seeds (out of sync since database restore)
DECLARE #c_Statement VARCHAR(MAX)
DECLARE StatementCursor CURSOR FOR
SELECT 'UPDATE [' + TABLE_SCHEMA + '].[' + TABLE_NAME + '] ' + 'SET [' + COLUMN_NAME + '] = RTRIM(LTRIM(SUBSTRING ([' + COLUMN_NAME + '], 2 , LEN ([' + COLUMN_NAME + ']) - 2) )) WHERE LEFT([' + COLUMN_NAME + '], 1) = ' + '''"''' + ' AND RIGHT([' + COLUMN_NAME + '], 1) = ' + '''"'''
FROM INFORMATION_SCHEMA.COLUMNS
WHERE DATA_TYPE IN ('varchar', 'nvarchar')
ORDER BY TABLE_NAME, COLUMN_NAME
OPEN StatementCursor
FETCH NEXT FROM StatementCursor INTO #c_Statement
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC(#c_Statement)
FETCH NEXT FROM StatementCursor INTO #c_Statement
END
CLOSE StatementCursor
DEALLOCATE StatementCursor
You may wanna check QUOTENAME() function to wrap object names in quotes instead of hard-coding them in the expression. You also need to check for errors (TRY/CATCH) and validate your user permissions to update the resulting rows.
If you have multiple databases, INFORMATION_SCHEMA.COLUMNS will only list the ones from the current connection.
I have 20 databases, each with same table but different columns.
So to make the uniform we are creating views on top of each table in a database which will contain all the columns, as there will be one application accessing all the database.
In the view, I have to write the query in such a way that if I want to alter it and add any addition column for testing I should be able to do that.
Now in below query I am altering / creating query such that it takes all the columns of that table from the database, and then I append the other columns which are not present in it.
I need to add a column which will just concatenate some of the columns
ALTER VIEW [dbo].[AIV_PARKING]
AS
SELECT
*,
Cast(NULL AS [VARCHAR](20)) [ACTCODE],
Cast(NULL AS [VARCHAR](1)) [ACTIVATEINFO],
Cast(NULL AS [VARCHAR](20)) [VEHLICNOCHECK],
Cast(NULL AS [VARCHAR](40)) [ACTIVITY],
Cast(Isnull(vehlicnocheck, '') + '|' +
Isnull(officername, '') + '|' +
Isnull(locstreet, '') + '|' +
Isnull(locsideofstreet, '') + '|' +
Isnull(loccrossstreet1, '') + '|' +
Isnull(loccrossstreet2, '') + '|'
+ Isnull(locsuburb, '') + '|'
+ Isnull(locstate, '') + '|'
+ Isnull(locpostalcode, '') + '|'
+ Isnull(loclot, '') + '|'
+ Isnull(locpostalcode, '') + '|'
+ Isnull(Cast(officerid AS VARCHAR(20)), '')
+ Isnull(officername, '') + '|'
+ Isnull(Cast (issueno AS VARCHAR(100)), '') AS NVARCHAR(max)) AS SearchText
FROM
[dbo].parking
Here I added a column called SearchText which concatenates other columns, but I get an error
Invalid column name 'VehLicNoCheck'
Is there any way I can add this column to this view?
I also tried to do to something below but I got the same error:
CAST(CASE
WHEN NOT EXISTS
(
Select 1 from INFORMATION_SCHEMA.COLUMNS
Where Column_name ='VehLicNoCheck'
and table_name='Parking'
)
THEN ''
ELSE ISNULL(VehLicNoCheck,'')
END as nvarchar(max)
)
you could create a view that normalizes the uncommon columns to rows, where the values for the common columns are just repeated, e.g:
select id, col, value from parking
unpivot (value for col in (actcode, vehLicNoCheck, etc.)) x
the code to dynamically generate the view would be something like:
declare #sql varchar(max) = 'select id, col, value from parking unpivot (value for col in ('
select #sql += quotename(name) +',' from sys.columns where object_id=object_id('parking') and name not in ('id')
set #sql = substring(#sql, 1, len(#sql) - 1) + '))x'
exec(#sql)
this does not make sense at all.
the [ACTCODE], [ACTIVATEINFO] are all NULL value
so basically SearchText is just a string of '|||||'
you might as well, just do this
SELECT *,
CAST( NULL AS varchar) [ACTCODE],
CAST( NULL AS varchar) [ACTIVATEINFO],
CAST( NULL AS varchar) [VEHLICNOCHECK],
CAST( NULL AS varchar) [ACTIVITY],
'||||||' as SearchText
FROM [dbo].PARKING
Maybe if you can explain what are you trying to achieve here, we can point you to the right direction
EDIT :
You will need to use Dynamic SQL. You will need a list of all column names
-- declare a table variable for all the columns that you required
declare #columns table
(
id int identity,
name varchar(100)
)
-- for example these are the required columns
insert into #columns
values ('ACTCODE'), ('ACTIVATEINFO'), ('VEHLICNOCHECK'), ('ACTIVITY')
-- The Query to create the view
declare #sql nvarchar(max)
select #sql = N'CREATE VIEW [AIV_PARKING] AS' + char(13) + 'SELECT' + char(13)
select #sql = #sql
+ case when t.name is not null
then quotename(c.name) + ','
else 'CAST (NULL AS VARCHAR(10)) AS ' + quotename(c.name) + ','
end
+ char(13)
from #columns c
left join sys.columns t on c.name = t.name
and t.object_id = object_id('PARKING')
order by c.id
select #sql = #sql
+ case when t.name is not null
then 'ISNULL(' + quotename(c.name) + ', '''')'
else ''
end
+ ' + ''|'''
+ ' + '
from #columns c
left join sys.columns t on c.name = t.name
and t.object_id = object_id('PARKING')
order by c.id
select #sql = left(#sql, len(#sql) - 8) + ' AS SearchText' + char(13)
+ 'FROM PARKING'
-- print out to view the complete create view statement
print #sql
-- execute it
exec sp_executesql #sql
I'm looking for a way to generate a "Create and insert all rows" script with SQL Management Studio 2008 R2.
I know that I can create a "create table" script.
I can also create an "insert in" script, but that will only generate a single row with placeholders.
Is there a way to generate an insert script that contains all currently stored rows?
Yes, but you'll need to run it at the database level.
Right-click the database in SSMS, select "Tasks", "Generate Scripts...". As you work through, you'll get to a "Scripting Options" section. Click on "Advanced", and in the list that pops up, where it says "Types of data to script", you've got the option to select Data and/or Schema.
This script generates insert statements of your existing data. This is a stored procedure which you need to run once and then it is tailor made for you.
I tried to find this kind of stuff for a while but wasn't satisfied with the results, so I wrote this stored procedure.
Example:
Exec [dbo].[INS] 'Dbo.test where 1=1'
(1) Here dbo is schema and test is tablename and 1=1 is condition.
Exec [dbo].[INS] 'Dbo.test where name =''neeraj''' * for string
(2) Here dbo is schema and test is tablename and name='neeraj' is condition.
Here is the stored procedure
/*
Authore : neeraj prasad sharma (please dont remove this :))
Example (1) Exec [dbo].[INS] 'Dbo.test where 1=1'
(2) Exec [dbo].[INS] 'Dbo.test where name =''neeraj''' * for string
here Dbo is schema and test is tablename and 1=1 is condition
*/
CREATE procedure [dbo].[INS]
(
#Query Varchar(MAX)
)
AS
SET nocount ON
DECLARE #WithStrINdex as INT
DECLARE #WhereStrINdex as INT
DECLARE #INDExtouse as INT
DECLARE #SchemaAndTAble VArchar(270)
DECLARE #Schema_name varchar(30)
DECLARE #Table_name varchar(240)
DECLARE #Condition Varchar(MAX)
SET #WithStrINdex=0
SELECT #WithStrINdex=CHARINDEX('With',#Query )
, #WhereStrINdex=CHARINDEX('WHERE', #Query)
IF(#WithStrINdex!=0)
SELECT #INDExtouse=#WithStrINdex
ELSE
SELECT #INDExtouse=#WhereStrINdex
SELECT #SchemaAndTAble=Left (#Query,#INDExtouse-1)
SELECT #SchemaAndTAble=Ltrim (Rtrim( #SchemaAndTAble))
SELECT #Schema_name= Left (#SchemaAndTAble, CharIndex('.',#SchemaAndTAble )-1)
, #Table_name = SUBSTRING( #SchemaAndTAble , CharIndex('.',#SchemaAndTAble )+1,LEN(#SchemaAndTAble) )
, #CONDITION=SUBSTRING(#Query,#WhereStrINdex+6,LEN(#Query))--27+6
DECLARE #COLUMNS table (Row_number SmallINT , Column_Name VArchar(Max) )
DECLARE #CONDITIONS as varchar(MAX)
DECLARE #Total_Rows as SmallINT
DECLARE #Counter as SmallINT
DECLARE #ComaCol as varchar(max)
SELECT #ComaCol=''
SET #Counter=1
SET #CONDITIONS=''
INSERT INTO #COLUMNS
SELECT Row_number()Over (Order by ORDINAL_POSITION ) [Count], Column_Name
FROM INformation_schema.columns
WHERE Table_schema=#Schema_name AND table_name=#Table_name
SELECT #Total_Rows= Count(1)
FROM #COLUMNS
SELECT #Table_name= '['+#Table_name+']'
SELECT #Schema_name='['+#Schema_name+']'
While (#Counter<=#Total_Rows )
begin
--PRINT #Counter
SELECT #ComaCol= #ComaCol+'['+Column_Name+'],'
FROM #COLUMNS
WHERE [Row_number]=#Counter
SELECT #CONDITIONS=#CONDITIONS+ ' + Case When ['+Column_Name+'] is null then ''Null'' Else '''''''' + Replace( Convert(varchar(Max),['+Column_Name+'] ) ,'''''''','''' ) +'''''''' end+'+''','''
FROM #COLUMNS
WHERE [Row_number]=#Counter
SET #Counter=#Counter+1
End
SELECT #CONDITIONS=Right(#CONDITIONS,LEN(#CONDITIONS)-2)
SELECT #CONDITIONS=LEFT(#CONDITIONS,LEN(#CONDITIONS)-4)
SELECT #ComaCol= substring (#ComaCol,0, len(#ComaCol) )
SELECT #CONDITIONS= '''INSERT INTO '+#Schema_name+'.'+#Table_name+ '('+#ComaCol+')' +' Values( '+'''' + '+'+#CONDITIONS
SELECT #CONDITIONS=#CONDITIONS+'+'+ ''')'''
SELECT #CONDITIONS= 'Select '+#CONDITIONS +'FRom ' +#Schema_name+'.'+#Table_name+' With(NOLOCK) ' + ' Where '+#Condition
print(#CONDITIONS)
Exec(#CONDITIONS)
Yes, use the commercial but inexpensive SSMS Tools Pack addin which has a nifty "Generate Insert statements from resultsets, tables or database" feature
Just to share, I've developed my own script to do it. Feel free to use it. It generates "SELECT" statements that you can then run on the tables to generate the "INSERT" statements.
select distinct 'SELECT ''INSERT INTO ' + schema_name(ta.schema_id) + '.' + so.name + ' (' + substring(o.list, 1, len(o.list)-1) + ') VALUES ('
+ substring(val.list, 1, len(val.list)-1) + ');'' FROM ' + schema_name(ta.schema_id) + '.' + so.name + ';'
from sys.objects so
join sys.tables ta on ta.object_id=so.object_id
cross apply
(SELECT ' ' +column_name + ', '
from information_schema.columns c
join syscolumns co on co.name=c.COLUMN_NAME and object_name(co.id)=so.name and OBJECT_NAME(co.id)=c.TABLE_NAME and co.id=so.object_id and c.TABLE_SCHEMA=SCHEMA_NAME(so.schema_id)
where table_name = so.name
order by ordinal_position
FOR XML PATH('')) o (list)
cross apply
(SELECT '''+' +case
when data_type = 'uniqueidentifier' THEN 'CASE WHEN [' + column_name+'] IS NULL THEN ''NULL'' ELSE ''''''''+CONVERT(NVARCHAR(MAX),[' + COLUMN_NAME + '])+'''''''' END '
WHEN data_type = 'timestamp' then '''''''''+CONVERT(NVARCHAR(MAX),CONVERT(BINARY(8),[' + COLUMN_NAME + ']),1)+'''''''''
WHEN data_type = 'nvarchar' then 'CASE WHEN [' + column_name+'] IS NULL THEN ''NULL'' ELSE ''''''''+REPLACE([' + COLUMN_NAME + '],'''''''','''''''''''')+'''''''' END'
WHEN data_type = 'varchar' then 'CASE WHEN [' + column_name+'] IS NULL THEN ''NULL'' ELSE ''''''''+REPLACE([' + COLUMN_NAME + '],'''''''','''''''''''')+'''''''' END'
WHEN data_type = 'char' then 'CASE WHEN [' + column_name+'] IS NULL THEN ''NULL'' ELSE ''''''''+REPLACE([' + COLUMN_NAME + '],'''''''','''''''''''')+'''''''' END'
WHEN data_type = 'nchar' then 'CASE WHEN [' + column_name+'] IS NULL THEN ''NULL'' ELSE ''''''''+REPLACE([' + COLUMN_NAME + '],'''''''','''''''''''')+'''''''' END'
when DATA_TYPE='datetime' then 'CASE WHEN [' + column_name+'] IS NULL THEN ''NULL'' ELSE ''''''''+CONVERT(NVARCHAR(MAX),[' + COLUMN_NAME + '],121)+'''''''' END '
when DATA_TYPE='datetime2' then 'CASE WHEN [' + column_name+'] IS NULL THEN ''NULL'' ELSE ''''''''+CONVERT(NVARCHAR(MAX),[' + COLUMN_NAME + '],121)+'''''''' END '
when DATA_TYPE='date' then 'CASE WHEN [' + column_name+'] IS NULL THEN ''NULL'' ELSE ''''''''+CONVERT(NVARCHAR(MAX),[' + COLUMN_NAME + '],121)+'''''''' END '
when DATA_TYPE='datetimeoffset' then 'CASE WHEN [' + column_name+'] IS NULL THEN ''NULL'' ELSE ''''''''+CONVERT(NVARCHAR(MAX),[' + COLUMN_NAME + '],121)+'''''''' END '
when DATA_TYPE='geography' and column_name<>'Shape' then 'ST_GeomFromText(''POINT('+column_name+'.Lat '+column_name+'.Long)'') '
when DATA_TYPE='geography' and column_name='Shape' then '''''''''+CONVERT(NVARCHAR(MAX),[' + COLUMN_NAME + '])+'''''''''
when DATA_TYPE='bit' then '''''''''+CONVERT(NVARCHAR(MAX),[' + COLUMN_NAME + '])+'''''''''
when DATA_TYPE='xml' then 'CASE WHEN [' + column_name+'] IS NULL THEN ''NULL'' ELSE ''''''''+REPLACE(CONVERT(NVARCHAR(MAX),[' + COLUMN_NAME + ']),'''''''','''''''''''')+'''''''' END '
when DATA_TYPE='text' then 'CASE WHEN [' + column_name+'] IS NULL THEN ''NULL'' ELSE ''''''''+REPLACE(CONVERT(NVARCHAR(MAX),[' + COLUMN_NAME + ']),'''''''','''''''''''')+'''''''' END '
WHEN DATA_TYPE='image' then 'CASE WHEN [' + column_name+'] IS NULL THEN ''NULL'' ELSE ''''''''+CONVERT(NVARCHAR(MAX),CONVERT(VARBINARY(MAX),[' + COLUMN_NAME + ']),1)+'''''''' END '
WHEN DATA_TYPE='varbinary' then 'CASE WHEN [' + column_name+'] IS NULL THEN ''NULL'' ELSE ''''''''+CONVERT(NVARCHAR(MAX),[' + COLUMN_NAME + '],1)+'''''''' END '
WHEN DATA_TYPE='binary' then 'CASE WHEN [' + column_name+'] IS NULL THEN ''NULL'' ELSE ''''''''+CONVERT(NVARCHAR(MAX),[' + COLUMN_NAME + '],1)+'''''''' END '
when DATA_TYPE='time' then 'CASE WHEN [' + column_name+'] IS NULL THEN ''NULL'' ELSE ''''''''+CONVERT(NVARCHAR(MAX),[' + COLUMN_NAME + '])+'''''''' END '
ELSE 'CASE WHEN [' + column_name+'] IS NULL THEN ''NULL'' ELSE CONVERT(NVARCHAR(MAX),['+column_name+']) END' end
+ '+'', '
from information_schema.columns c
join syscolumns co on co.name=c.COLUMN_NAME and object_name(co.id)=so.name and OBJECT_NAME(co.id)=c.TABLE_NAME and co.id=so.object_id and c.TABLE_SCHEMA=SCHEMA_NAME(so.schema_id)
where table_name = so.name
order by ordinal_position
FOR XML PATH('')) val (list)
where so.type = 'U'
I've spent a good amount of time coming up with solution to this problem, so in the spirit of this post, I'm posting it here, since I think it might be useful to others.
If anyone has a better script, or anything to add, please post it.
Edit: Yes guys, I know how to do it in Management Studio - but I needed to be able to do it from within another application.
I've modified the version above to run for all tables and support new SQL 2005 data types. It also retains the primary key names. Works only on SQL 2005 (using cross apply).
select 'create table [' + so.name + '] (' + o.list + ')' + CASE WHEN tc.Constraint_Name IS NULL THEN '' ELSE 'ALTER TABLE ' + so.Name + ' ADD CONSTRAINT ' + tc.Constraint_Name + ' PRIMARY KEY ' + ' (' + LEFT(j.List, Len(j.List)-1) + ')' END
from sysobjects so
cross apply
(SELECT
' ['+column_name+'] ' +
data_type + case data_type
when 'sql_variant' then ''
when 'text' then ''
when 'ntext' then ''
when 'xml' then ''
when 'decimal' then '(' + cast(numeric_precision as varchar) + ', ' + cast(numeric_scale as varchar) + ')'
else coalesce('('+case when character_maximum_length = -1 then 'MAX' else cast(character_maximum_length as varchar) end +')','') end + ' ' +
case when exists (
select id from syscolumns
where object_name(id)=so.name
and name=column_name
and columnproperty(id,name,'IsIdentity') = 1
) then
'IDENTITY(' +
cast(ident_seed(so.name) as varchar) + ',' +
cast(ident_incr(so.name) as varchar) + ')'
else ''
end + ' ' +
(case when UPPER(IS_NULLABLE) = 'NO' then 'NOT ' else '' end ) + 'NULL ' +
case when information_schema.columns.COLUMN_DEFAULT IS NOT NULL THEN 'DEFAULT '+ information_schema.columns.COLUMN_DEFAULT ELSE '' END + ', '
from information_schema.columns where table_name = so.name
order by ordinal_position
FOR XML PATH('')) o (list)
left join
information_schema.table_constraints tc
on tc.Table_name = so.Name
AND tc.Constraint_Type = 'PRIMARY KEY'
cross apply
(select '[' + Column_Name + '], '
FROM information_schema.key_column_usage kcu
WHERE kcu.Constraint_Name = tc.Constraint_Name
ORDER BY
ORDINAL_POSITION
FOR XML PATH('')) j (list)
where xtype = 'U'
AND name NOT IN ('dtproperties')
Update: Added handling of the XML data type
Update 2: Fixed cases when 1) there is multiple tables with the same name but with different schemas, 2) there is multiple tables having PK constraint with the same name
Here's the script that I came up with. It handles Identity columns, default values, and primary keys. It does not handle foreign keys, indexes, triggers, or any other clever stuff. It works on SQLServer 2000, 2005 and 2008.
declare #schema varchar(100), #table varchar(100)
set #schema = 'dbo' -- set schema name here
set #table = 'MyTable' -- set table name here
declare #sql table(s varchar(1000), id int identity)
-- create statement
insert into #sql(s) values ('create table [' + #table + '] (')
-- column list
insert into #sql(s)
select
' ['+column_name+'] ' +
data_type + coalesce('('+cast(character_maximum_length as varchar)+')','') + ' ' +
case when exists (
select id from syscolumns
where object_name(id)=#table
and name=column_name
and columnproperty(id,name,'IsIdentity') = 1
) then
'IDENTITY(' +
cast(ident_seed(#table) as varchar) + ',' +
cast(ident_incr(#table) as varchar) + ')'
else ''
end + ' ' +
( case when IS_NULLABLE = 'No' then 'NOT ' else '' end ) + 'NULL ' +
coalesce('DEFAULT '+COLUMN_DEFAULT,'') + ','
from INFORMATION_SCHEMA.COLUMNS where table_name = #table AND table_schema = #schema
order by ordinal_position
-- primary key
declare #pkname varchar(100)
select #pkname = constraint_name from INFORMATION_SCHEMA.TABLE_CONSTRAINTS
where table_name = #table and constraint_type='PRIMARY KEY'
if ( #pkname is not null ) begin
insert into #sql(s) values(' PRIMARY KEY (')
insert into #sql(s)
select ' ['+COLUMN_NAME+'],' from INFORMATION_SCHEMA.KEY_COLUMN_USAGE
where constraint_name = #pkname
order by ordinal_position
-- remove trailing comma
update #sql set s=left(s,len(s)-1) where id=##identity
insert into #sql(s) values (' )')
end
else begin
-- remove trailing comma
update #sql set s=left(s,len(s)-1) where id=##identity
end
-- closing bracket
insert into #sql(s) values( ')' )
-- result!
select s from #sql order by id
There is a Powershell script buried in the msdb forums that will script all the tables and related objects:
# Script all tables in a database
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO")
| out-null
$s = new-object ('Microsoft.SqlServer.Management.Smo.Server') '<Servername>'
$db = $s.Databases['<Database>']
$scrp = new-object ('Microsoft.SqlServer.Management.Smo.Scripter') ($s)
$scrp.Options.AppendToFile = $True
$scrp.Options.ClusteredIndexes = $True
$scrp.Options.DriAll = $True
$scrp.Options.ScriptDrops = $False
$scrp.Options.IncludeHeaders = $False
$scrp.Options.ToFileOnly = $True
$scrp.Options.Indexes = $True
$scrp.Options.WithDependencies = $True
$scrp.Options.FileName = 'C:\Temp\<Database>.SQL'
foreach($item in $db.Tables) { $tablearray+=#($item) }
$scrp.Script($tablearray)
Write-Host "Scripting complete"
Support for schemas:
This is an updated version that amends the great answer from David, et al. Added is support for named schemas. It should be noted this may break if there's actually tables of the same name present within various schemas. Another improvement is the use of the official QuoteName() function.
SELECT
t.TABLE_CATALOG,
t.TABLE_SCHEMA,
t.TABLE_NAME,
'create table '+QuoteName(t.TABLE_SCHEMA)+'.' + QuoteName(so.name) + ' (' + LEFT(o.List, Len(o.List)-1) + '); '
+ CASE WHEN tc.Constraint_Name IS NULL THEN ''
ELSE
'ALTER TABLE ' + QuoteName(t.TABLE_SCHEMA)+'.' + QuoteName(so.name)
+ ' ADD CONSTRAINT ' + tc.Constraint_Name + ' PRIMARY KEY ' + ' (' + LEFT(j.List, Len(j.List)-1) + '); '
END as 'SQL_CREATE_TABLE'
FROM sysobjects so
CROSS APPLY (
SELECT
' ['+column_name+'] '
+ data_type
+ case data_type
when 'sql_variant' then ''
when 'text' then ''
when 'ntext' then ''
when 'decimal' then '(' + cast(numeric_precision as varchar) + ', ' + cast(numeric_scale as varchar) + ')'
else
coalesce(
'('+ case when character_maximum_length = -1
then 'MAX'
else cast(character_maximum_length as varchar) end
+ ')','')
end
+ ' '
+ case when exists (
SELECT id
FROM syscolumns
WHERE
object_name(id) = so.name
and name = column_name
and columnproperty(id,name,'IsIdentity') = 1
) then
'IDENTITY(' +
cast(ident_seed(so.name) as varchar) + ',' +
cast(ident_incr(so.name) as varchar) + ')'
else ''
end
+ ' '
+ (case when IS_NULLABLE = 'No' then 'NOT ' else '' end)
+ 'NULL '
+ case when information_schema.columns.COLUMN_DEFAULT IS NOT NULL THEN 'DEFAULT '+ information_schema.columns.COLUMN_DEFAULT
ELSE ''
END
+ ',' -- can't have a field name or we'll end up with XML
FROM information_schema.columns
WHERE table_name = so.name
ORDER BY ordinal_position
FOR XML PATH('')
) o (list)
LEFT JOIN information_schema.table_constraints tc on
tc.Table_name = so.Name
AND tc.Constraint_Type = 'PRIMARY KEY'
LEFT JOIN information_schema.tables t on
t.Table_name = so.Name
CROSS APPLY (
SELECT QuoteName(Column_Name) + ', '
FROM information_schema.key_column_usage kcu
WHERE kcu.Constraint_Name = tc.Constraint_Name
ORDER BY ORDINAL_POSITION
FOR XML PATH('')
) j (list)
WHERE
xtype = 'U'
AND name NOT IN ('dtproperties')
-- AND so.name = 'ASPStateTempSessions'
;
..
For use in Management Studio:
One detractor to the sql code above is if you test it using SSMS, long statements aren't easy to read. So, as per this helpful post, here's another version that's somewhat modified to be easier on the eyes after clicking the link of a cell in the grid. The results are more readily identifiable as nicely formatted CREATE TABLE statements for each table in the db.
-- settings
DECLARE #CRLF NCHAR(2)
SET #CRLF = Nchar(13) + NChar(10)
DECLARE #PLACEHOLDER NCHAR(3)
SET #PLACEHOLDER = '{:}'
-- the main query
SELECT
t.TABLE_CATALOG,
t.TABLE_SCHEMA,
t.TABLE_NAME,
CAST(
REPLACE(
'create table ' + QuoteName(t.TABLE_SCHEMA) + '.' + QuoteName(so.name) + ' (' + #CRLF
+ LEFT(o.List, Len(o.List) - (LEN(#PLACEHOLDER)+2)) + #CRLF + ');' + #CRLF
+ CASE WHEN tc.Constraint_Name IS NULL THEN ''
ELSE
'ALTER TABLE ' + QuoteName(t.TABLE_SCHEMA) + '.' + QuoteName(so.Name)
+ ' ADD CONSTRAINT ' + tc.Constraint_Name + ' PRIMARY KEY (' + LEFT(j.List, Len(j.List) - 1) + ');' + #CRLF
END,
#PLACEHOLDER,
#CRLF
)
AS XML) as 'SQL_CREATE_TABLE'
FROM sysobjects so
CROSS APPLY (
SELECT
' '
+ '['+column_name+'] '
+ data_type
+ case data_type
when 'sql_variant' then ''
when 'text' then ''
when 'ntext' then ''
when 'decimal' then '(' + cast(numeric_precision as varchar) + ', ' + cast(numeric_scale as varchar) + ')'
else
coalesce(
'('+ case when character_maximum_length = -1
then 'MAX'
else cast(character_maximum_length as varchar) end
+ ')','')
end
+ ' '
+ case when exists (
SELECT id
FROM syscolumns
WHERE
object_name(id) = so.name
and name = column_name
and columnproperty(id,name,'IsIdentity') = 1
) then
'IDENTITY(' +
cast(ident_seed(so.name) as varchar) + ',' +
cast(ident_incr(so.name) as varchar) + ')'
else ''
end
+ ' '
+ (case when IS_NULLABLE = 'No' then 'NOT ' else '' end)
+ 'NULL '
+ case when information_schema.columns.COLUMN_DEFAULT IS NOT NULL THEN 'DEFAULT '+ information_schema.columns.COLUMN_DEFAULT
ELSE ''
END
+ ', '
+ #PLACEHOLDER -- note, can't have a field name or we'll end up with XML
FROM information_schema.columns where table_name = so.name
ORDER BY ordinal_position
FOR XML PATH('')
) o (list)
LEFT JOIN information_schema.table_constraints tc on
tc.Table_name = so.Name
AND tc.Constraint_Type = 'PRIMARY KEY'
LEFT JOIN information_schema.tables t on
t.Table_name = so.Name
CROSS APPLY (
SELECT QUOTENAME(Column_Name) + ', '
FROM information_schema.key_column_usage kcu
WHERE kcu.Constraint_Name = tc.Constraint_Name
ORDER BY ORDINAL_POSITION
FOR XML PATH('')
) j (list)
WHERE
xtype = 'U'
AND name NOT IN ('dtproperties')
-- AND so.name = 'ASPStateTempSessions'
;
Not to belabor the point, but here's the functionally equivalent example outputs for comparison:
-- 1 (scripting version)
create table [dbo].[ASPStateTempApplications] ( [AppId] int NOT NULL , [AppName] char(280) NOT NULL ); ALTER TABLE [dbo].[ASPStateTempApplications] ADD CONSTRAINT PK__ASPState__8E2CF7F908EA5793 PRIMARY KEY ([AppId]);
-- 2 (SSMS version)
create table [dbo].[ASPStateTempSessions] (
[SessionId] nvarchar(88) NOT NULL ,
[Created] datetime NOT NULL DEFAULT (getutcdate()),
[Expires] datetime NOT NULL ,
[LockDate] datetime NOT NULL ,
[LockDateLocal] datetime NOT NULL ,
[LockCookie] int NOT NULL ,
[Timeout] int NOT NULL ,
[Locked] bit NOT NULL ,
[SessionItemShort] varbinary(7000) NULL ,
[SessionItemLong] image(2147483647) NULL ,
[Flags] int NOT NULL DEFAULT ((0))
);
ALTER TABLE [dbo].[ASPStateTempSessions] ADD CONSTRAINT PK__ASPState__C9F4929003317E3D PRIMARY KEY ([SessionId]);
..
Detracting factors:
It should be noted that I remain relatively unhappy with this due to the lack of support for indeces other than a primary key. It remains suitable for use as a mechanism for simple data export or replication.
If the application you are generating the scripts from is a .NET application, you may want to look into using SMO (Sql Management Objects). Reference this SQL Team link on how to use SMO to script objects.
One more variant with foreign keys support and in one statement:
SELECT
obj.name
,'CREATE TABLE [' + obj.name + '] (' + LEFT(cols.list, LEN(cols.list) - 1 ) + ')'
+ ISNULL(' ' + refs.list, '')
FROM sysobjects obj
CROSS APPLY (
SELECT
CHAR(10)
+ ' [' + column_name + '] '
+ data_type
+ CASE data_type
WHEN 'sql_variant' THEN ''
WHEN 'text' THEN ''
WHEN 'ntext' THEN ''
WHEN 'xml' THEN ''
WHEN 'decimal' THEN '(' + CAST(numeric_precision as VARCHAR) + ', ' + CAST(numeric_scale as VARCHAR) + ')'
ELSE COALESCE('(' + CASE WHEN character_maximum_length = -1 THEN 'MAX' ELSE CAST(character_maximum_length as VARCHAR) END + ')', '')
END
+ ' '
+ case when exists ( -- Identity skip
select id from syscolumns
where object_name(id) = obj.name
and name = column_name
and columnproperty(id,name,'IsIdentity') = 1
) then
'IDENTITY(' +
cast(ident_seed(obj.name) as varchar) + ',' +
cast(ident_incr(obj.name) as varchar) + ')'
else ''
end + ' '
+ CASE WHEN IS_NULLABLE = 'No' THEN 'NOT ' ELSE '' END
+ 'NULL'
+ CASE WHEN information_schema.columns.column_default IS NOT NULL THEN ' DEFAULT ' + information_schema.columns.column_default ELSE '' END
+ ','
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE table_name = obj.name
ORDER BY ordinal_position
FOR XML PATH('')
) cols (list)
CROSS APPLY(
SELECT
CHAR(10) + 'ALTER TABLE ' + obj.name + '_noident_temp ADD ' + LEFT(alt, LEN(alt)-1)
FROM(
SELECT
CHAR(10)
+ ' CONSTRAINT ' + tc.constraint_name
+ ' ' + tc.constraint_type + ' (' + LEFT(c.list, LEN(c.list)-1) + ')'
+ COALESCE(CHAR(10) + r.list, ', ')
FROM
information_schema.table_constraints tc
CROSS APPLY(
SELECT
'[' + kcu.column_name + '], '
FROM
information_schema.key_column_usage kcu
WHERE
kcu.constraint_name = tc.constraint_name
ORDER BY
kcu.ordinal_position
FOR XML PATH('')
) c (list)
OUTER APPLY(
-- // http://stackoverflow.com/questions/3907879/sql-server-howto-get-foreign-key-reference-from-information-schema
SELECT
' REFERENCES [' + kcu1.constraint_schema + '].' + '[' + kcu2.table_name + ']' + '(' + kcu2.column_name + '), '
FROM information_schema.referential_constraints as rc
JOIN information_schema.key_column_usage as kcu1 ON (kcu1.constraint_catalog = rc.constraint_catalog AND kcu1.constraint_schema = rc.constraint_schema AND kcu1.constraint_name = rc.constraint_name)
JOIN information_schema.key_column_usage as kcu2 ON (kcu2.constraint_catalog = rc.unique_constraint_catalog AND kcu2.constraint_schema = rc.unique_constraint_schema AND kcu2.constraint_name = rc.unique_constraint_name AND kcu2.ordinal_position = KCU1.ordinal_position)
WHERE
kcu1.constraint_catalog = tc.constraint_catalog AND kcu1.constraint_schema = tc.constraint_schema AND kcu1.constraint_name = tc.constraint_name
) r (list)
WHERE tc.table_name = obj.name
FOR XML PATH('')
) a (alt)
) refs (list)
WHERE
xtype = 'U'
AND name NOT IN ('dtproperties')
AND obj.name = 'your_table_name'
You could try in is sqlfiddle: http://sqlfiddle.com/#!6/e3b66/3/0
I modified the accepted answer and now it can get the command including primary key and foreign key in a certain schema.
declare #table varchar(100)
declare #schema varchar(100)
set #table = 'Persons' -- set table name here
set #schema = 'OT' -- set SCHEMA name here
declare #sql table(s varchar(1000), id int identity)
-- create statement
insert into #sql(s) values ('create table ' + #table + ' (')
-- column list
insert into #sql(s)
select
' '+column_name+' ' +
data_type + coalesce('('+cast(character_maximum_length as varchar)+')','') + ' ' +
case when exists (
select id from syscolumns
where object_name(id)=#table
and name=column_name
and columnproperty(id,name,'IsIdentity') = 1
) then
'IDENTITY(' +
cast(ident_seed(#table) as varchar) + ',' +
cast(ident_incr(#table) as varchar) + ')'
else ''
end + ' ' +
( case when IS_NULLABLE = 'No' then 'NOT ' else '' end ) + 'NULL ' +
coalesce('DEFAULT '+COLUMN_DEFAULT,'') + ','
from information_schema.columns where table_name = #table and table_schema = #schema
order by ordinal_position
-- primary key
declare #pkname varchar(100)
select #pkname = constraint_name from information_schema.table_constraints
where table_name = #table and constraint_type='PRIMARY KEY'
if ( #pkname is not null ) begin
insert into #sql(s) values(' PRIMARY KEY (')
insert into #sql(s)
select ' '+COLUMN_NAME+',' from information_schema.key_column_usage
where constraint_name = #pkname
order by ordinal_position
-- remove trailing comma
update #sql set s=left(s,len(s)-1) where id=##identity
insert into #sql(s) values (' )')
end
else begin
-- remove trailing comma
update #sql set s=left(s,len(s)-1) where id=##identity
end
-- foreign key
declare #fkname varchar(100)
select #fkname = constraint_name from information_schema.table_constraints
where table_name = #table and constraint_type='FOREIGN KEY'
if ( #fkname is not null ) begin
insert into #sql(s) values(',')
insert into #sql(s) values(' FOREIGN KEY (')
insert into #sql(s)
select ' '+COLUMN_NAME+',' from information_schema.key_column_usage
where constraint_name = #fkname
order by ordinal_position
-- remove trailing comma
update #sql set s=left(s,len(s)-1) where id=##identity
insert into #sql(s) values (' ) REFERENCES ')
insert into #sql(s)
SELECT
OBJECT_NAME(fk.referenced_object_id)
FROM
sys.foreign_keys fk
INNER JOIN
sys.foreign_key_columns fkc ON fkc.constraint_object_id = fk.object_id
INNER JOIN
sys.columns c1 ON fkc.parent_column_id = c1.column_id AND fkc.parent_object_id = c1.object_id
INNER JOIN
sys.columns c2 ON fkc.referenced_column_id = c2.column_id AND fkc.referenced_object_id = c2.object_id
where fk.name = #fkname
insert into #sql(s)
SELECT
'('+c2.name+')'
FROM
sys.foreign_keys fk
INNER JOIN
sys.foreign_key_columns fkc ON fkc.constraint_object_id = fk.object_id
INNER JOIN
sys.columns c1 ON fkc.parent_column_id = c1.column_id AND fkc.parent_object_id = c1.object_id
INNER JOIN
sys.columns c2 ON fkc.referenced_column_id = c2.column_id AND fkc.referenced_object_id = c2.object_id
where fk.name = #fkname
end
-- closing bracket
insert into #sql(s) values( ')' )
-- result!
select s from #sql order by id
I'm going to improve the answer by supporting partitioned tables:
find partition scheme and partition key using below scritps:
declare #partition_scheme varchar(100) = (
select distinct ps.Name AS PartitionScheme
from sys.indexes i
join sys.partitions p ON i.object_id=p.object_id AND i.index_id=p.index_id
join sys.partition_schemes ps on ps.data_space_id = i.data_space_id
where i.object_id = object_id('your table name')
)
print #partition_scheme
declare #partition_column varchar(100) = (
select c.name
from sys.tables t
join sys.indexes i
on(i.object_id = t.object_id
and i.index_id < 2)
join sys.index_columns ic
on(ic.partition_ordinal > 0
and ic.index_id = i.index_id and ic.object_id = t.object_id)
join sys.columns c
on(c.object_id = ic.object_id
and c.column_id = ic.column_id)
where t.object_id = object_id('your table name')
)
print #partition_column
then change the generation query by adding below line at the right place:
+ IIF(#partition_scheme is null, '', 'ON [' + #partition_scheme + ']([' + #partition_column + '])')
Credit due to #Blorgbeard for sharing his script. I'll certainly bookmark it in case I need it.
Yes, you can "right click" on the table and script the CREATE TABLE script, but:
The a script will contain loads of cruft (interested in the extended properties anyone?)
If you have 200+ tables in your schema, it's going to take you half a day to script the lot by hand.
With this script converted into a stored procedure, and combined with a wrapper script you would have a nice automated way to dump your table design into source control etc.
The rest of your DB code (SP's, FK indexes, Triggers etc) would be under source control anyway ;)
Something I've noticed - in the INFORMATION_SCHEMA.COLUMNS view, CHARACTER_MAXIMUM_LENGTH gives a size of 2147483647 (2^31-1) for field types such as image and text. ntext is 2^30-1 (being double-byte unicode and all).
This size is included in the output from this query, but it is invalid for these data types in a CREATE statement (they should not have a maximum size value at all). So unless the results from this are manually corrected, the CREATE script won't work given these data types.
I imagine it's possible to fix the script to account for this, but that's beyond my SQL capabilities.
-- or you could create a stored procedure ... first with Id creation
USE [db]
GO
/****** Object: StoredProcedure [dbo].[procUtils_InsertGeneratorWithId] Script Date: 06/13/2009 22:18:11 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
create PROC [dbo].[procUtils_InsertGeneratorWithId]
(
#domain_user varchar(50),
#tableName varchar(100)
)
as
--Declare a cursor to retrieve column specific information for the specified table
DECLARE cursCol CURSOR FAST_FORWARD FOR
SELECT column_name,data_type FROM information_schema.columns WHERE table_name = #tableName
OPEN cursCol
DECLARE #string nvarchar(3000) --for storing the first half of INSERT statement
DECLARE #stringData nvarchar(3000) --for storing the data (VALUES) related statement
DECLARE #dataType nvarchar(1000) --data types returned for respective columns
DECLARE #IDENTITY_STRING nvarchar ( 100 )
SET #IDENTITY_STRING = ' '
select #IDENTITY_STRING
SET #string='INSERT '+#tableName+'('
SET #stringData=''
DECLARE #colName nvarchar(50)
FETCH NEXT FROM cursCol INTO #colName,#dataType
IF ##fetch_status<>0
begin
print 'Table '+#tableName+' not found, processing skipped.'
close curscol
deallocate curscol
return
END
WHILE ##FETCH_STATUS=0
BEGIN
IF #dataType in ('varchar','char','nchar','nvarchar')
BEGIN
--SET #stringData=#stringData+'''''''''+isnull('+#colName+','''')+'''''',''+'
SET #stringData=#stringData+''''+'''+isnull('''''+'''''+'+#colName+'+'''''+''''',''NULL'')+'',''+'
END
ELSE
if #dataType in ('text','ntext') --if the datatype is text or something else
BEGIN
SET #stringData=#stringData+'''''''''+isnull(cast('+#colName+' as varchar(2000)),'''')+'''''',''+'
END
ELSE
IF #dataType = 'money' --because money doesn't get converted from varchar implicitly
BEGIN
SET #stringData=#stringData+'''convert(money,''''''+isnull(cast('+#colName+' as varchar(200)),''0.0000'')+''''''),''+'
END
ELSE
IF #dataType='datetime'
BEGIN
--SET #stringData=#stringData+'''convert(datetime,''''''+isnull(cast('+#colName+' as varchar(200)),''0'')+''''''),''+'
--SELECT 'INSERT Authorizations(StatusDate) VALUES('+'convert(datetime,'+isnull(''''+convert(varchar(200),StatusDate,121)+'''','NULL')+',121),)' FROM Authorizations
--SET #stringData=#stringData+'''convert(money,''''''+isnull(cast('+#colName+' as varchar(200)),''0.0000'')+''''''),''+'
SET #stringData=#stringData+'''convert(datetime,'+'''+isnull('''''+'''''+convert(varchar(200),'+#colName+',121)+'''''+''''',''NULL'')+'',121),''+'
-- 'convert(datetime,'+isnull(''''+convert(varchar(200),StatusDate,121)+'''','NULL')+',121),)' FROM Authorizations
END
ELSE
IF #dataType='image'
BEGIN
SET #stringData=#stringData+'''''''''+isnull(cast(convert(varbinary,'+#colName+') as varchar(6)),''0'')+'''''',''+'
END
ELSE --presuming the data type is int,bit,numeric,decimal
BEGIN
--SET #stringData=#stringData+'''''''''+isnull(cast('+#colName+' as varchar(200)),''0'')+'''''',''+'
--SET #stringData=#stringData+'''convert(datetime,'+'''+isnull('''''+'''''+convert(varchar(200),'+#colName+',121)+'''''+''''',''NULL'')+'',121),''+'
SET #stringData=#stringData+''''+'''+isnull('''''+'''''+convert(varchar(200),'+#colName+')+'''''+''''',''NULL'')+'',''+'
END
SET #string=#string+#colName+','
FETCH NEXT FROM cursCol INTO #colName,#dataType
END
DECLARE #Query nvarchar(4000)
SET #query ='SELECT '''+substring(#string,0,len(#string)) + ') VALUES(''+ ' + substring(#stringData,0,len(#stringData)-2)+'''+'')'' FROM '+#tableName
exec sp_executesql #query
--select #query
CLOSE cursCol
DEALLOCATE cursCol
/*
USAGE
*/
GO
-- and second without iD INSERTION
USE [db]
GO
/****** Object: StoredProcedure [dbo].[procUtils_InsertGenerator] Script Date: 06/13/2009 22:20:52 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROC [dbo].[procUtils_InsertGenerator]
(
#domain_user varchar(50),
#tableName varchar(100)
)
as
--Declare a cursor to retrieve column specific information for the specified table
DECLARE cursCol CURSOR FAST_FORWARD FOR
-- SELECT column_name,data_type FROM information_schema.columns WHERE table_name = #tableName
/* NEW
SELECT c.name , sc.data_type FROM sys.extended_properties AS ep
INNER JOIN sys.tables AS t ON ep.major_id = t.object_id
INNER JOIN sys.columns AS c ON ep.major_id = c.object_id AND ep.minor_id
= c.column_id
INNER JOIN INFORMATION_SCHEMA.COLUMNS sc ON t.name = sc.table_name and
c.name = sc.column_name
WHERE t.name = #tableName and c.is_identity=0
*/
select object_name(c.object_id) "TABLE_NAME", c.name "COLUMN_NAME", s.name "DATA_TYPE"
from sys.columns c
join sys.systypes s on (s.xtype = c.system_type_id)
where object_name(c.object_id) in (select name from sys.tables where name not like 'sysdiagrams')
AND object_name(c.object_id) in (select name from sys.tables where [name]=#tableName ) and c.is_identity=0 and s.name not like 'sysname'
OPEN cursCol
DECLARE #string nvarchar(3000) --for storing the first half of INSERT statement
DECLARE #stringData nvarchar(3000) --for storing the data (VALUES) related statement
DECLARE #dataType nvarchar(1000) --data types returned for respective columns
DECLARE #IDENTITY_STRING nvarchar ( 100 )
SET #IDENTITY_STRING = ' '
select #IDENTITY_STRING
SET #string='INSERT '+#tableName+'('
SET #stringData=''
DECLARE #colName nvarchar(50)
FETCH NEXT FROM cursCol INTO #tableName , #colName,#dataType
IF ##fetch_status<>0
begin
print 'Table '+#tableName+' not found, processing skipped.'
close curscol
deallocate curscol
return
END
WHILE ##FETCH_STATUS=0
BEGIN
IF #dataType in ('varchar','char','nchar','nvarchar')
BEGIN
--SET #stringData=#stringData+'''''''''+isnull('+#colName+','''')+'''''',''+'
SET #stringData=#stringData+''''+'''+isnull('''''+'''''+'+#colName+'+'''''+''''',''NULL'')+'',''+'
END
ELSE
if #dataType in ('text','ntext') --if the datatype is text or something else
BEGIN
SET #stringData=#stringData+'''''''''+isnull(cast('+#colName+' as varchar(2000)),'''')+'''''',''+'
END
ELSE
IF #dataType = 'money' --because money doesn't get converted from varchar implicitly
BEGIN
SET #stringData=#stringData+'''convert(money,''''''+isnull(cast('+#colName+' as varchar(200)),''0.0000'')+''''''),''+'
END
ELSE
IF #dataType='datetime'
BEGIN
--SET #stringData=#stringData+'''convert(datetime,''''''+isnull(cast('+#colName+' as varchar(200)),''0'')+''''''),''+'
--SELECT 'INSERT Authorizations(StatusDate) VALUES('+'convert(datetime,'+isnull(''''+convert(varchar(200),StatusDate,121)+'''','NULL')+',121),)' FROM Authorizations
--SET #stringData=#stringData+'''convert(money,''''''+isnull(cast('+#colName+' as varchar(200)),''0.0000'')+''''''),''+'
SET #stringData=#stringData+'''convert(datetime,'+'''+isnull('''''+'''''+convert(varchar(200),'+#colName+',121)+'''''+''''',''NULL'')+'',121),''+'
-- 'convert(datetime,'+isnull(''''+convert(varchar(200),StatusDate,121)+'''','NULL')+',121),)' FROM Authorizations
END
ELSE
IF #dataType='image'
BEGIN
SET #stringData=#stringData+'''''''''+isnull(cast(convert(varbinary,'+#colName+') as varchar(6)),''0'')+'''''',''+'
END
ELSE --presuming the data type is int,bit,numeric,decimal
BEGIN
--SET #stringData=#stringData+'''''''''+isnull(cast('+#colName+' as varchar(200)),''0'')+'''''',''+'
--SET #stringData=#stringData+'''convert(datetime,'+'''+isnull('''''+'''''+convert(varchar(200),'+#colName+',121)+'''''+''''',''NULL'')+'',121),''+'
SET #stringData=#stringData+''''+'''+isnull('''''+'''''+convert(varchar(200),'+#colName+')+'''''+''''',''NULL'')+'',''+'
END
SET #string=#string+#colName+','
FETCH NEXT FROM cursCol INTO #tableName , #colName,#dataType
END
DECLARE #Query nvarchar(4000)
SET #query ='SELECT '''+substring(#string,0,len(#string)) + ') VALUES(''+ ' + substring(#stringData,0,len(#stringData)-2)+'''+'')'' FROM '+#tableName
exec sp_executesql #query
--select #query
CLOSE cursCol
DEALLOCATE cursCol
/*
use poc
go
DECLARE #RC int
DECLARE #domain_user varchar(50)
DECLARE #tableName varchar(100)
-- TODO: Set parameter values here.
set #domain_user='yorgeorg'
set #tableName = 'tbGui_WizardTabButtonAreas'
EXECUTE #RC = [POC].[dbo].[procUtils_InsertGenerator]
#domain_user
,#tableName
*/
GO
Show create table in classic asp (handles constraints, primary keys, copying the table structure and/or data ...)
Sql server Show create table
Mysql-style "Show create table" and "show create database" commands from Microsoft sql server.
The script is written is Microsoft asp-language and is quite easy to port to another language.*
I include definitions for computed columns
select 'CREATE TABLE [' + so.name + '] (' + o.list + ')' + CASE WHEN tc.Constraint_Name IS NULL THEN '' ELSE 'ALTER TABLE ' + so.Name + ' ADD CONSTRAINT ' + tc.Constraint_Name + ' PRIMARY KEY ' + ' (' + LEFT(j.List, Len(j.List)-1) + ')' END, name
from sysobjects so
cross apply
(SELECT
case when comps.definition is not null then ' ['+column_name+'] AS ' + comps.definition
else
' ['+column_name+'] ' + data_type +
case
when data_type like '%text' or data_type in ('image', 'sql_variant' ,'xml')
then ''
when data_type in ('float')
then '(' + cast(coalesce(numeric_precision, 18) as varchar(11)) + ')'
when data_type in ('datetime2', 'datetimeoffset', 'time')
then '(' + cast(coalesce(datetime_precision, 7) as varchar(11)) + ')'
when data_type in ('decimal', 'numeric')
then '(' + cast(coalesce(numeric_precision, 18) as varchar(11)) + ',' + cast(coalesce(numeric_scale, 0) as varchar(11)) + ')'
when (data_type like '%binary' or data_type like '%char') and character_maximum_length = -1
then '(max)'
when character_maximum_length is not null
then '(' + cast(character_maximum_length as varchar(11)) + ')'
else ''
end + ' ' +
case when exists (
select id from syscolumns
where object_name(id)=so.name
and name=column_name
and columnproperty(id,name,'IsIdentity') = 1
) then
'IDENTITY(' +
cast(ident_seed(so.name) as varchar) + ',' +
cast(ident_incr(so.name) as varchar) + ')'
else ''
end + ' ' +
(case when information_schema.columns.IS_NULLABLE = 'No' then 'NOT ' else '' end ) + 'NULL ' +
case when information_schema.columns.COLUMN_DEFAULT IS NOT NULL THEN 'DEFAULT '+ information_schema.columns.COLUMN_DEFAULT ELSE '' END
end + ', '
from information_schema.columns
left join sys.computed_columns comps
on OBJECT_ID(information_schema.columns.TABLE_NAME)=comps.object_id and information_schema.columns.COLUMN_NAME=comps.name
where table_name = so.name
order by ordinal_position
FOR XML PATH('')) o (list)
left join
information_schema.table_constraints tc
on tc.Table_name = so.Name
AND tc.Constraint_Type = 'PRIMARY KEY'
cross apply
(select '[' + Column_Name + '], '
FROM information_schema.key_column_usage kcu
WHERE kcu.Constraint_Name = tc.Constraint_Name
ORDER BY
ORDINAL_POSITION
FOR XML PATH('')) j (list)
where xtype = 'U'
AND name NOT IN ('dtproperties')
I realise that it's been a very long time but thought I'd add anyway. If you just want the table, and not the create table statement you could use
select into x from db.schema.y where 1=0
to copy the table to a new DB
A query based on Hubbitus answer.
includes schema names
fixes foreign keys with more than one field
includes CASCADE UPDATE & DELETE
includes a conditioned DROP TABLE
SELECT
Schema_Name = SCHEMA_NAME(obj.uid)
, Table_Name = name
, Drop_Table = 'IF (EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = ''' + SCHEMA_NAME(obj.uid) + ''' AND TABLE_NAME = ''' + obj.name + '''))
DROP TABLE [' + SCHEMA_NAME(obj.uid) + '].[' + obj.name + '] '
, Create_Table ='
CREATE TABLE [' + SCHEMA_NAME(obj.uid) + '].[' + obj.name + '] (' + LEFT(cols.list, LEN(cols.list) - 1 ) + ')' + ISNULL(' ' + refs.list, '')
FROM sysobjects obj
CROSS APPLY (
SELECT
CHAR(10)
+ ' [' + column_name + '] '
+ data_type
+ CASE data_type
WHEN 'sql_variant' THEN ''
WHEN 'text' THEN ''
WHEN 'ntext' THEN ''
WHEN 'xml' THEN ''
WHEN 'decimal' THEN '(' + CAST(numeric_precision as VARCHAR) + ', ' + CAST(numeric_scale as VARCHAR) + ')'
ELSE COALESCE('(' + CASE WHEN character_maximum_length = -1 THEN 'MAX' ELSE CAST(character_maximum_length as VARCHAR) END + ')', '')
END
+ ' '
+ case when exists ( -- Identity skip
select id from syscolumns
where id = obj.id
and name = column_name
and columnproperty(id, name, 'IsIdentity') = 1
) then
'IDENTITY(' +
cast(ident_seed(obj.name) as varchar) + ',' +
cast(ident_incr(obj.name) as varchar) + ')'
else ''
end + ' '
+ CASE WHEN IS_NULLABLE = 'No' THEN 'NOT ' ELSE '' END
+ 'NULL'
+ CASE WHEN IC.column_default IS NOT NULL THEN ' DEFAULT ' + IC.column_default ELSE '' END
+ ','
FROM INFORMATION_SCHEMA.COLUMNS IC
WHERE IC.table_name = obj.name
AND IC.TABLE_SCHEMA = SCHEMA_NAME(obj.uid)
ORDER BY ordinal_position
FOR XML PATH('')
) cols (list)
CROSS APPLY(
SELECT
CHAR(10) + 'ALTER TABLE [' + SCHEMA_NAME(obj.uid) + '].[' + obj.name + '] ADD ' + LEFT(alt, LEN(alt)-1)
FROM(
SELECT
CHAR(10)
+ ' CONSTRAINT ' + tc.constraint_name
+ ' ' + tc.constraint_type + ' (' + LEFT(c.list, LEN(c.list)-1) + ')'
+ COALESCE(CHAR(10) + r.list, ', ')
FROM information_schema.table_constraints tc
CROSS APPLY(
SELECT '[' + kcu.column_name + '], '
FROM information_schema.key_column_usage kcu
WHERE kcu.constraint_name = tc.constraint_name
ORDER BY kcu.ordinal_position
FOR XML PATH('')
) c (list)
OUTER APPLY(
-- // http://stackoverflow.com/questions/3907879/sql-server-howto-get-foreign-key-reference-from-information-schema
SELECT LEFT(f.list, LEN(f.list)-1) + ')' + IIF(rc.DELETE_RULE = 'NO ACTION', '', ' ON DELETE ' + rc.DELETE_RULE) + IIF(rc.UPDATE_RULE = 'NO ACTION', '', ' ON UPDATE ' + rc.UPDATE_RULE) + ', '
FROM information_schema.referential_constraints rc
CROSS APPLY(
SELECT IIF(kcu.ordinal_position = 1, ' REFERENCES [' + kcu.table_schema + '].[' + kcu.table_name + '] (', '')
+ '[' + kcu.column_name + '], '
FROM information_schema.key_column_usage kcu
WHERE kcu.constraint_catalog = rc.unique_constraint_catalog AND kcu.constraint_schema = rc.unique_constraint_schema AND kcu.constraint_name = rc.unique_constraint_name
ORDER BY kcu.ordinal_position
FOR XML PATH('')
) f (list)
WHERE rc.constraint_catalog = tc.constraint_catalog
AND rc.constraint_schema = tc.constraint_schema
AND rc.constraint_name = tc.constraint_name
) r (list)
WHERE tc.table_name = obj.name
FOR XML PATH('')
) a (alt)
) refs (list)
WHERE xtype = 'U'
To combine drop table (if exists) with create use like this:
SELECT Drop_Table + CHAR(10) + Create_Table FROM SysCreateTables
If you are using management studio and have the query analyzer window open you can drag the table name to the query analyzer window and ... bingo! you get the table script.
I've not tried this in SQL2008