xquery loop over columns - sql-server-2005

I have been tasked with creating a service broker using Xquery to handle tracking changes on a collection of tables. I have figured out how to pass the messages (xml of column names and the updated and deleted tables for the statements). The aim is to get the list of column names and then compare the like column for each updated/deleted row and not a change.
Here is a sample of the XML:
<Update xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<TableName>
<ID>2414</ID>
<fkEvent>2664</fkEvent>
<fkType xsi:nil="true" />
<Description>Phil Test 3</Description>
<DTS>2011-04-04T14:01:36.533</DTS>
<uID>192204FA-612F-46F4-A6CB-1B4D53769A81</uID>
<VersionID xsi:nil="true" />
<UpdateDateTime>2011-04-04T14:04:31.013</UpdateDateTime>
<DeleteFlag>0</DeleteFlag>
<Updated>0</Updated>
<Owner>42</Owner>
<CreatedBy>42</CreatedBy>
</TableName>
</Update>
Generated by:
SET #xml1 = (SELECT * FROM TableName ORDER BY ID DESC FOR XML AUTO, ELEMENTS XSINIL, ROOT('MsgEnv'))
I have the following code:
WHILE #cnt <= #totCnt BEGIN
SELECT #child = #ColNames.query('/Columns/name[position()=sql:variable("#cnt")]')
SET #CurrentCol = REPLACE(REPLACE(CAST(#child AS VARCHAR(500)), '<name>', ''), '</name>', '')
PRINT #CurrentCol
WHILE #updateCnt <= #updateCntTotal BEGIN
SELECT #childUpdate = #xml1.query('/Update/TableName/sql:variable("#CurrentCol")')
PRINT CAST(#childUpdate AS VARCHAR(MAX))
WHILE #deleteCnt <= #deleteCntTotal BEGIN
SELECT #deleteCnt = #deleteCnt + 1
END
SET #deleteCnt = 1
SELECT #updateCnt = #updateCnt + 1
END
SET #updateCnt = 1
SELECT #cnt = #cnt + 1
END
The trouble I am having is dynamically setting the column name for this statement:
SELECT #childUpdate = #xml1.query('/Update/TableName/sql:variable("#CurrentCol")')
I have tried a few different variations using the sql:variable. Is it not possible to do this? I'd like to be able to do this dynamically as there are lots of tables we need to "audit" changes on.
Edit 1:
SELECT #childUpdate = #xml1.query('/Update/TableName/*[name() = sql:variable("#CurrentCol")]')
Yields this error (including the . in the () has a similar effect.
Msg 2395, Level 16, State 1, Line 34
XQuery [query()]: There is no function '{http://www.w3.org/2004/07/xpath-functions}:name()'

Your XQuery expression:
/Update/TableName/sql:variable("#CurrentCol")
It will call sql:variable() extension function for each /Update/TableName element.
If you want to select TableName's child with the same name as the string result of your extension function, then use:
/Update/TableName/*[name(.) = sql:variable("#CurrentCol")]

The previous answer didn't help at all but here is what I have found to work for this situation. The trigger will pass in 4 XML strings. The first contains the column information, the next two are the XML contents of the INSERTED and DELETED temporary tables, and the last is a Meta string (schema name, table name, updated by user, timestamp, etc).
Here is what the column XML code looks like:
DECLARE #ColNames XML
DECLARE #ColumnTypeInfo TABLE (
column_name varchar(100),
data_type varchar(100))
INSERT INTO #ColumnTypeInfo (column_name,data_type)
(
SELECT column_name 'column_name',
CASE WHEN
DATA_TYPE = 'datetime' OR DATA_TYPE = 'int' OR DATA_TYPE = 'bit' OR
DATA_TYPE = 'uniqueidentifier' OR DATA_TYPE = 'sql_variant'
THEN DATA_TYPE ELSE
CASE WHEN CHARACTER_MAXIMUM_LENGTH IS NOT NULL THEN
data_type + '(' +
CAST(CHARACTER_MAXIMUM_LENGTH AS VARCHAR(10))
+ ')'
ELSE
CASE WHEN NUMERIC_PRECISION IS NOT NULL AND NUMERIC_SCALE IS NOT NULL THEN
data_type + '(' +
CAST(NUMERIC_PRECISION AS VARCHAR(10))
+ ',' +
CAST(NUMERIC_SCALE AS VARCHAR(10))
+ ')'
ELSE
DATA_TYPE
END
END
END 'data_type'
FROM information_schema.columns WHERE table_name = 'tbl_ActivityPart'
)
SET #ColNames = (
SELECT * FROM #ColumnTypeInfo
FOR XML PATH ('Column'), ROOT('ColumnDef')
)
#ColNames is passed into the message queue.
This is the basis for the procedure that processes the queued messages:
WHILE #cnt <= #totCnt BEGIN
SET #CurrentCol = CAST(#ColNames.query('for $b in /ColumnDef/Column[position()=sql:variable("#cnt")]/column_name return ($b)') AS VARCHAR(MAX))
SET #CurrentCol = REPLACE(REPLACE(#CurrentCol, '<column_name>', ''), '</column_name>', '')
SET #DataType = CAST(#ColNames.query('for $b in /ColumnDef/Column[position()=sql:variable("#cnt")]/data_type return ($b)') AS VARCHAR(MAX))
SET #DataType = REPLACE(REPLACE(#DataType, '<data_type>', ''), '</data_type>', '')
SET #updateQuery = '/Update/Scheme.TableName/'+#CurrentCol
SET #SQL = 'SELECT #TmpXML = #UpdatedXML.query(''' + #updateQuery + ''')'
EXEC sp_executesql #SQL, N'#UpdatedXML xml, #TmpXML XML output', #UpdatedXML, #TmpXML output
SET #childUpdate = #TmpXML
SET #NewValue = REPLACE(REPLACE(CAST(#childUpdate AS VARCHAR(8000)), '<'+#CurrentCol+'>', ''), '</'+#CurrentCol+'>', '')
IF (CHARINDEX('xsi:nil="true"', CONVERT(VARCHAR(8000), #NewValue)) <> 0) BEGIN
SET #NewValue = NULL
END
SET #deleteQuery = '/Delete/Scheme.TableName/'+#CurrentCol
SET #SQL = 'SELECT #TmpXML = #DeletedXML.query(''' + #deleteQuery + ''')'
EXEC sp_executesql #SQL, N'#DeletedXML xml, #TmpXML XML output', #DeletedXML, #TmpXML output
SET #childDelete = #TmpXML
SET #OldValue = REPLACE(REPLACE(CAST(#childDelete AS VARCHAR(8000)), '<'+#CurrentCol+'>', ''), '</'+#CurrentCol+'>', '')
IF (CHARINDEX('xsi:nil="true"', CONVERT(VARCHAR(8000), #OldValue)) <> 0) BEGIN
SET #OldValue = NULL
END
IF #NewValue <> #OldValue BEGIN
INSERT INTO #Changes (SchemaName, TableName, FieldName, DTS,
[uID], OldValue, NewValue, ValueDataType, [User])
SELECT #Schema, #TableName, #CurrentCol, #TimeStamp,
CONVERT(UNIQUEIDENTIFIER, #CurrentUID), #OldValue, #NewValue, #DataType, #UpdateUserID
END
-- **********************************************************************************************************
SELECT #cnt = #cnt + 1
END
The contents of #Changes is then inserted into the permanent table (which is now on a separate disk volume from the rest of the tables in that database).

Related

Variable value was lost when while loop finished

I want to set some default values to some table by using dynamic SQL in SQL Server, so I write 2 while loop, one is for tables and one is for columns in that table. so the outer loop is used to iterate table and the inner loop is used to iterate columns according to different data types the default will vary from one to other. So I need to catenate strings to build the dynamic SQL, please see my code below:
DECLARE #V_TABLE_LIST TABLE (TABLE_NAME VARCHAR(300))
DECLARE #V_COLUMN_LIST TABLE (TABLE_NAME VARCHAR(300), COLUMN_NAME VARCHAR(300), DATA_TYPE VARCHAR(300))
DECLARE #V_TABLE_NAME VARCHAR(300)
DECLARE #V_TABLE_NAME2 VARCHAR(300)
DECLARE #V_COLUMN_NAME VARCHAR(300)
DECLARE #V_DATA_TYPE VARCHAR(300)
DECLARE #V_SQL_ENABLE_IDENTITY_INSERT VARCHAR(200)
DECLARE #V_SQL_INSERT VARCHAR(3500)
DECLARE #V_SQL_COLUMN_LIST_NAME VARCHAR(3000)
DECLARE #V_SQL_COLUMN_LIST_VALUE VARCHAR(3000)
DECLARE #V_SQL_DISABLE_IDENTITY_INSERT VARCHAR(200)
INSERT INTO #V_TABLE_LIST
(TABLE_NAME)
SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE 'DIM%' AND TABLE_TYPE = 'BASE TABLE'
AND TABLE_NAME NOT IN ('DIM_DATE') AND TABLE_NAME = 'DIM_ASSET'
--loop through each table
WHILE (SELECT COUNT(*) FROM #V_TABLE_LIST) > 0
BEGIN
SELECT TOP 1
#V_TABLE_NAME = TABLE_NAME
FROM #V_TABLE_LIST
--PRINT(#V_TABLE_NAME)-------------
SET #V_SQL_ENABLE_IDENTITY_INSERT = 'SET IDENTITY_INSERT ' + #V_TABLE_NAME + ' ON'
SET #V_SQL_DISABLE_IDENTITY_INSERT = 'SET IDENTITY_INSERT ' + #V_TABLE_NAME + ' OFF'
--load column info into #v_column_list table variable for each table
INSERT INTO #V_COLUMN_LIST
(TABLE_NAME, COLUMN_NAME, DATA_TYPE)
SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE
FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #V_TABLE_NAME
SET #V_SQL_INSERT = ''
SET #V_SQL_COLUMN_LIST_NAME = ''
SET #V_SQL_COLUMN_LIST_VALUE = ''
--loop through each column for each table
WHILE (SELECT COUNT(*) FROM #V_COLUMN_LIST) > 0
BEGIN
SELECT TOP 1
#V_TABLE_NAME2 = TABLE_NAME
,#V_COLUMN_NAME = COLUMN_NAME
,#V_DATA_TYPE = DATA_TYPE
FROM #V_COLUMN_LIST
SET #V_SQL_COLUMN_LIST_NAME = #V_SQL_COLUMN_LIST_NAME + #V_COLUMN_NAME + ' --' + #V_DATA_TYPE +CHAR(10) + ','
SET #V_SQL_COLUMN_LIST_VALUE = #V_SQL_COLUMN_LIST_VALUE +
CASE WHEN #V_DATA_TYPE IN ('VARCHAR','NVARCHAR','CHAR', 'NCHAR') THEN '''UNKNOWN'''
WHEN #V_DATA_TYPE IN ('bigint', 'INT', 'smallint', 'DECIMAL','NUMERIC','MONEY','SMALLMONEY') THEN '-1'
WHEN #V_DATA_TYPE IN ('BIT', 'TINYINT') THEN NULL
WHEN #V_DATA_TYPE IN ('DATE', 'DATETIME','SMALLDATETIME','DATETIMEOFFSET','DATETIME2') THEN '''1957-01-01'''
ELSE ''
END + ' --' + #V_COLUMN_NAME + CHAR(10) + ','
DELETE FROM #V_COLUMN_LIST WHERE TABLE_NAME = #V_TABLE_NAME2 AND COLUMN_NAME = #V_COLUMN_NAME
--PRINT(#V_SQL_COLUMN_LIST_VALUE)
END
PRINT(#V_SQL_COLUMN_LIST_NAME)
PRINT(#V_SQL_COLUMN_LIST_VALUE)
--PRINT(#V_SQL_ENABLE_IDENTITY_INSERT)
SET #V_SQL_INSERT = 'INSERT INTO ' + #V_TABLE_NAME + CHAR(10)
+ '('
+ #V_SQL_COLUMN_LIST_NAME
+ ')'
+ ' VALUES ' + CHAR(10)
+ '(' + CHAR(10)
+ #V_SQL_COLUMN_LIST_VALUE
+ ')'
--PRINT(#V_SQL_INSERT)
--PRINT(#V_SQL_DISABLE_IDENTITY_INSERT)
DELETE FROM #V_COLUMN_LIST
DELETE FROM #V_TABLE_LIST WHERE TABLE_NAME = #V_TABLE_NAME
END
I added 2 print statements:
PRINT(#V_SQL_COLUMN_LIST_NAME) ---the concatenated field list can be printed out normally
PRINT(#V_SQL_COLUMN_LIST_VALUE) ---cannot print concatenated default value list , why?
as you can see the two print statements are the next step for the finishing of inner loop, but the first print statement can print out the something and the second one is empty, I checked the code a long time, I cannot find why the second print statement output empty string. Any logic errors in the code above?
This row sets the entire result to NULL if any column of BIT or TINYINT type is met.
WHEN #V_DATA_TYPE IN ('BIT', 'TINYINT') THEN NULL
Should be
WHEN #V_DATA_TYPE IN ('BIT', 'TINYINT') THEN 'NULL'
the same way as any other constant in a dynamic sql.

SQL Server 2012 - Trigger: Operand type clash: int is incompatible with unique identifier

I am writing an audit trigger which saves every change occurred in the table in another table. But I receive this error when I am trying to alter the Users table:
Operand type clash: int is incompatible with unique identifier
The Users table contains a uniqueidentifier column, among others.
Here is the part of the code where the error occurs:
ALTER TRIGGER [dbo].[UsersLogger]
ON [dbo].[Users]
FOR INSERT, DELETE, UPDATE
AS
BEGIN
DECLARE #audit_oldvalue sql_variant;
DECLARE #audit_value sql_variant;
DECLARE #audit_field varchar(100);
DECLARE #sql nvarchar(max);
DECLARE #ParmDefinition nvarchar(max) ;
DECLARE #OutString varchar(max) ;
DECLARE #converted_uid nvarchar(50);
DECLARE #qid int ;
DECLARE #Cinfo VARBINARY(128)
SELECT #Cinfo = Context_Info()
IF #Cinfo = 0x55555
RETURN
DECLARE #Action as char(1);
SET #Action = (CASE WHEN EXISTS(SELECT * FROM INSERTED)
AND EXISTS(SELECT * FROM DELETED)
THEN 'U' -- Set Action to Updated.
ELSE NULL -- Skip. It may have been a "failed delete".
END)
SET #OutString = ''
SELECT *
INTO #tempTrigT
FROM
(SELECT *
FROM deleted
WHERE #Action IN ( 'U', 'D')) A
UNION
(SELECT *
FROM inserted
WHERE #Action = 'I')
SET #sql = ''
if #Action = 'U'
BEGIN
Select #sql = #sql + 'Case when IsNull(i.[' + Column_Name +
'],0) = IsNull(d.[' + Column_name + '],0) then ''''
else ' + quotename(Column_Name, char(39)) + ' + '',''' + ' end +'
from information_schema.columns
where table_name = 'Users' and column_name <>'rowguid' and column_name <>'modifieddate'
set #ParmDefinition = '#OutString varchar(max) OUTPUT'
set #sql = 'Select #OutString = '
+ Substring(#sql,1 , len(#sql) -1) +
' From dbo.Users i '
+ ' inner join #tempTrigT d on
i.id = d.id'
exec sp_executesql #sql, #ParmDefinition, #OutString OUT
END
DECLARE #Items VARCHAR(max)
set #Items = #OutString;
DECLARE #Item VARCHAR(50)
DECLARE #Pos INT
DECLARE #Loop BIT
SELECT #Loop = CASE WHEN LEN(#OutString) > 0 THEN 1 ELSE 0 END
WHILE (SELECT #Loop) = 1
BEGIN
SELECT #Pos = CHARINDEX(',', #OutString, 1)
IF #Pos > 0
BEGIN
SELECT #Item = SUBSTRING(#OutString, 1, #Pos - 1)
SELECT #OutString = SUBSTRING(#OutString, #Pos + 1, LEN(#OutString) - #Pos)
IF (TRY_CONVERT(UNIQUEIDENTIFIER, #Item) is not null)
begin
select #Item = convert(nvarchar(50), #Item)
end
If I had to guess, I'd blame it on
Select #sql = #sql + 'Case when IsNull(i.[' + Column_Name +
'],0) = IsNull(d.[' + Column_name + '],0) then ''''
else ' + quotename(Column_Name, char(39)) + ' + '',''' + ' end +'
from information_schema.columns
where table_name = 'Users' and column_name <>'rowguid' and column_name <>'modifieddate'
It's because you're doing an ISNULL on a UNIQUEIDENTIIFIER column and setting the value to an INT. You could change from ISNULL(i.[ColumnName], 0) to ISNULL(i.[ColumnName], '') however, that is assuming all the columns in your tables are string applicable datatypes. Otherwise, you could do some conditional logic on the replacement value for the ISNULL function by using the [DATA_TYPE] column in that table.

using information_schema_tables and concatenate

I want to know how can I use the information_schema_tables select query to look up #tablename, so that, that table's catalog and schema is shown, and then concatenate it together so that #tablename is displayed as table_catalog.table_schema.table name'?
At the moment I am just calling on the table name using select #tablename = Value
declare #tablename varchar(MAX)
declare #tableschema varchar(MAX)
declare #loop int = 1
select a.* into #tmp
from
(
select RID,
v.value('local-name(.)', 'VARCHAR(MAX)') 'Field',
v.value('./text()[1]', 'VARCHAR(MAX)') 'Value'
from #XMLTemp
cross apply Field.nodes ('/Record/*') x(v)
where v.value('local-name(.)', 'VARCHAR(MAX)') not in ('Update', 'Filter', 'Insert', 'Delete')
) as a
where RID = #loop
...
select Table_Catalog, Table_Schema
from Information_Schema.Tables
...
select #tablename = ''
select #tablename = Value
from #tmp
where Field='tableName'
and RID = #loop
...
print 'update ' + #tablename + '
...
select #tablename = Value from #tmp where Field = 'TableName'
...
set #loop = #loop+1
In SQL Server you can use "+" to concatenate strings.
declare #tablename varchar(MAX)
select #tablename = TABLE_CATALOG + '.' + TABLE_SCHEMA + '.' + TABLE_NAME
from INFORMATION_SCHEMA.TABLES
where TABLE_NAME = 'TableName'
Keep in mind that if your query returns multiple rows #tablename variable will contains the last value returned.
select quotename(db_name()) + '.' + quotename( schemas.name ) + '.' + quotename( tables.name )
from sys.tables
join sys.schemas on tables.schema_id = schemas.schema_id
A couple of notes: "Catalog" in ANSI speak is Database in SQL Server, so within a database it's pretty much a constant value - the name of the current database.
In SQL Server I find the system views are more consistent and reliable than INFORMATION_SCHEMA, which mostly works but has some quirky issues.
According to your last question I'd like to suggest the following UDF:
You pass in your XML and a catalog's name (or NULLor DEFAULT) and the same with the schema's name. The function will use COALESCE to use the right portion:
CREATE FUNCTION dbo.CreateUpdateStatement
(
#XmlData XML
,#CatalogName VARCHAR(100) = NULL
,#SchemaName VARCHAR(100) = NULL
)
RETURNS VARCHAR(MAX)
BEGIN
DECLARE #RetVal VARCHAR(MAX);
WITH XMLNAMESPACES('http://www.w3.org/2001/XMLSchema-instance' AS xsi)
SELECT #RetVal=
'UPDATE '
+ COALESCE(#CatalogName + '.',TheTable.TABLE_CATALOG + '.', '')
+ COALESCE(#SchemaName + '.',TheTable.TABLE_SCHEMA + '.', 'dbo.')
+ One.Record.value('TableName[1]','varchar(max)')
+ ' SET ' + One.Record.value('(Update/FieldName)[1]','varchar(max)') + '=''' + One.Record.value('(Update/NewValue)[1]','varchar(max)') + ''' '
+ ' WHERE ' + One.Record.value('KeyField[1]','varchar(max)') + '=''' + One.Record.value('TableRef[1]','varchar(max)') + ''';'
FROM #XmlData.nodes('/Task/Record') AS One(Record)
OUTER APPLY
(
SELECT TOP 1 TABLE_CATALOG,TABLE_SCHEMA,TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME=One.Record.value('TableName[1]','varchar(max)')
) AS TheTable;
RETURN #RetVal;
END
GO
This is how you call it (I used one existing table's name spz.dbo.AuditRow in one of my catalogs):
DECLARE #x xml=
'<Task xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Record>
<order>1</order>
<TableName>AuditRow</TableName>
<KeyField>ProductPersonID</KeyField>
<TableRef>32420</TableRef>
<Update>
<FieldName>StatusID</FieldName>
<OldValue>3</OldValue>
<NewValue>8</NewValue>
</Update>
</Record>
</Task>';
SELECT dbo.CreateUpdateStatement(#x,DEFAULT,DEFAULT);
--UPDATE spz.dbo.AuditRow SET StatusID='8' WHERE ProductPersonID='32420';
SELECT dbo.CreateUpdateStatement(#x,'MyCatalog',DEFAULT);
--UPDATE MyCatalog.dbo.AuditRow SET StatusID='8' WHERE ProductPersonID='32420';
SELECT dbo.CreateUpdateStatement(#x,DEFAULT,'MySchema');
--UPDATE spz.MySchema.AuditRow SET StatusID='8' WHERE ProductPersonID='32420';
SELECT dbo.CreateUpdateStatement(#x,'MyCatalog','MySchema');
--UPDATE MyCatalog.MySchema.AuditRow SET StatusID='8' WHERE ProductPersonID='32420';
You might execute this immediately with
EXEC (SELECT dbo.CreateUpdateStatement(#x,NULL,NULL));

Converting Select results into Insert script - SQL Server

I have SQL Server 2008, SQL Server Management Studio.
I need to select data from a table in one database and insert into another table in another database.
How can I convert the returned results from my select into INSERT INTO ...?
Clarification from comments: While I believe this could be solved by a INSERT INTO SELECT or SELECT INTO, I do need to generate INSERT INTO ....
Here is another method, which may be easier than installing plugins or external tools in some situations:
Do a select [whatever you need]INTO temp.table_namefrom [... etc ...].
Right-click on the database in the Object Explorer => Tasks => Generate Scripts
Select temp.table_name in the "Choose Objects" screen, click Next.
In the "Specify how scripts should be saved" screen:
Click Advanced, find the "Types of data to Script" property, select "Data only", close the advanced properties.
Select "Save to new query window" (unless you have thousands of records).
Click Next, wait for the job to complete, observe the resulting INSERT statements appear in a new query window.
Use Find & Replace to change all [temp.table_name] to [your_table_name].
drop table [temp.table_name].
In SSMS:
Right click on the database > Tasks > Generate Scripts
Next
Select "Select specific database objects" and check the table you want scripted, Next
Click Advanced > in the list of options, scroll down to the bottom and look for the "Types of data to script" and change it to "Data Only" > OK
Select "Save to new query window" > Next > Next > Finish
All 180 rows now written as 180 insert statements!
Native method:
for example if you have table
Users(Id, name)
You can do this:
select 'insert into Table values(Id=' + Id + ', name=' + name + ')' from Users
1- Explanation of Scripts
A)Syntax for inserting data in table is as below
Insert into table(col1,col2,col3,col4,col5)
-- To achieve this part i
--have used below variable
------#CSV_COLUMN-------
values(Col1 data in quote, Col2..quote,..Col5..quote)
-- To achieve this part
-- i.e column data in
--quote i have used
--below variable
----#QUOTED_DATA---
C)To get above data from existing
table we have to write the select
query in such way that the output
will be in form of as above scripts
D)Then Finally i have Concatenated
above variable to create
final script that's will
generate insert script on execution
E)
#TEXT='SELECT ''INSERT INTO
'+#TABLE_NAME+'('+#CSV_COLUMN+')VALUES('''+'+'+SUBSTRING(#QUOTED_DATA,1,LEN(#QUOTED_DATA)-5)+'+'+''')'''+' Insert_Scripts FROM '+#TABLE_NAME + #FILTER_CONDITION
F)And Finally Executed the above query EXECUTE(TEXT)
G)QUOTENAME() function is used to wrap
column data inside quote
H)ISNULL is used because if any row has NULL
data for any column the query fails
and return NULL thats why to avoid
that i have used ISNULL
I)And created the sp sp_generate_insertscripts
for same
1- Just put the table name for which you want insert script
2- Filter condition if you want specific results
----------Final Procedure To generate Script------
CREATE PROCEDURE sp_generate_insertscripts
(
#TABLE_NAME VARCHAR(MAX),
#FILTER_CONDITION VARCHAR(MAX)=''
)
AS
BEGIN
SET NOCOUNT ON
DECLARE #CSV_COLUMN VARCHAR(MAX),
#QUOTED_DATA VARCHAR(MAX),
#TEXT VARCHAR(MAX)
SELECT #CSV_COLUMN=STUFF
(
(
SELECT ',['+ NAME +']' FROM sys.all_columns
WHERE OBJECT_ID=OBJECT_ID(#TABLE_NAME) AND
is_identity!=1 FOR XML PATH('')
),1,1,''
)
SELECT #QUOTED_DATA=STUFF
(
(
SELECT ' ISNULL(QUOTENAME('+NAME+','+QUOTENAME('''','''''')+'),'+'''NULL'''+')+'','''+'+' FROM sys.all_columns
WHERE OBJECT_ID=OBJECT_ID(#TABLE_NAME) AND
is_identity!=1 FOR XML PATH('')
),1,1,''
)
SELECT #TEXT='SELECT ''INSERT INTO '+#TABLE_NAME+'('+#CSV_COLUMN+')VALUES('''+'+'+SUBSTRING(#QUOTED_DATA,1,LEN(#QUOTED_DATA)-5)+'+'+''')'''+' Insert_Scripts FROM '+#TABLE_NAME + #FILTER_CONDITION
--SELECT #CSV_COLUMN AS CSV_COLUMN,#QUOTED_DATA AS QUOTED_DATA,#TEXT TEXT
EXECUTE (#TEXT)
SET NOCOUNT OFF
END
SSMS Toolpack (which is FREE as in beer) has a variety of great features - including generating INSERT statements from tables.
Update: for SQL Server Management Studio 2012 (and newer), SSMS Toolpack is no longer free, but requires a modest licensing fee.
It's possible to do via Visual Studio SQL Server Object Explorer.
You can click "View Data" from context menu for necessary table, filter results and save result as script.
Using visual studio, do the following
Create a project of type SQL Server-->SQL Server Database Project
open the sql server explorer CTL-\ , CTL-S
add a SQL Server by right clicking on the SQL SERVER icon. Selcet ADD NEW SERVER
navigate down to the table you are interested in
right click--> VIEW DATA
Click the top left cell to highlight everything (ctl-A doesnt seem to work)
Right Click -->SCript
This is fabulous. I have tried everything listed above over the years. I know there is a tool out there that will do this and much more, cant think of the name of it. But it is very expensive.
Good luck. I just figured this out. Have not tested it extensively w/ text fields etc, but it looks like it gets you a long ways down the road.
Greg
Create a separate table using into statement
For example
Select * into Test_123 from [dbo].[Employee] where Name like '%Test%'
Go to the Database
Right Click the Database
Click on Generate Script
Select your table
Select advanace option and select the Attribute "Data Only"
Select the file "open in new query"
Sql will generate script for you
This is a more versatile solution (that can do a little more than the question asks), and can be used in a query window without having to create a new stored proc - useful in production databases for instance where you don't have write access.
To use the code, please modify according to the in line comments which explain its usage. You can then just run this query in a query window and it will print the INSERT statements you require.
SET NOCOUNT ON
-- Set the ID you wish to filter on here
DECLARE #id AS INT = 123
DECLARE #tables TABLE (Name NVARCHAR(128), IdField NVARCHAR(128), IdInsert BIT, Excluded NVARCHAR(128))
-- Add any tables you wish to generate INSERT statements for here. The fields are as thus:
-- Name: Your table name
-- IdField: The field on which to filter the dataset
-- IdInsert: If the primary key field is to be included in the INSERT statement
-- Excluded: Any fields you do not wish to include in the INSERT statement
INSERT INTO #tables (Name, IdField, IdInsert, Excluded) VALUES ('MyTable1', 'Id', 0, 'Created,Modified')
INSERT INTO #tables (Name, IdField, IdInsert, Excluded) VALUES ('MyTable2', 'Id', 1, 'Created,Modified')
DECLARE #numberTypes TABLE (sysId TINYINT)
-- This will ensure INT and BIT types are not surrounded with quotes in the
-- resultant INSERT statement, but you may need to add more (from sys.types)
INSERT #numberTypes(SysId) VALUES(56),(104)
DECLARE #rows INT = (SELECT COUNT(*) FROM #tables)
DECLARE #cnt INT = 1
DECLARE #results TABLE (Sql NVARCHAR(4000))
WHILE #cnt <= #rows
BEGIN
DECLARE #tablename AS NVARCHAR(128)
DECLARE #idField AS NVARCHAR(128)
DECLARE #idInsert AS BIT
DECLARE #excluded AS NVARCHAR(128)
SELECT
#tablename = Name,
#idField = IdField,
#idInsert = IdInsert,
#excluded = Excluded
FROM (SELECT *, ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS RowId FROM #tables) t WHERE t.RowId = #cnt
DECLARE #excludedFields TABLE (FieldName NVARCHAR(128))
DECLARE #xml AS XML = CAST(('<X>' + REPLACE(#excluded, ',', '</X><X>') + '</X>') AS XML)
INSERT INTO #excludedFields SELECT N.value('.', 'NVARCHAR(128)') FROM #xml.nodes('X') AS T(N)
DECLARE #setIdentity NVARCHAR(128) = 'SET IDENTITY_INSERT ' + #tablename
DECLARE #execsql AS NVARCHAR(4000) = 'SELECT ''' + CASE WHEN #idInsert = 1 THEN #setIdentity + ' ON' + CHAR(13) ELSE '' END + 'INSERT INTO ' + #tablename + ' ('
SELECT #execsql = #execsql +
STUFF
(
(
SELECT CASE WHEN NOT EXISTS(SELECT * FROM #excludedFields WHERE FieldName = name) THEN ', ' + name ELSE '' END
FROM sys.columns
WHERE object_id = OBJECT_ID('dbo.' + #tablename)
FOR XML PATH('')
), 1, 2, ''
) +
')' + CHAR(13) + 'VALUES (' +
STUFF
(
(
SELECT
CASE WHEN NOT EXISTS(SELECT * FROM #excludedFields WHERE FieldName = name) THEN
''', '' + ISNULL(' +
CASE WHEN EXISTS(SELECT * FROM #numberTypes WHERE SysId = system_type_id) THEN '' ELSE ''''''''' + ' END +
'CAST(' + name + ' AS VARCHAR)' +
CASE WHEN EXISTS(SELECT * FROM #numberTypes WHERE SysId = system_type_id) THEN '' ELSE ' + ''''''''' END +
', ''NULL'') + '
ELSE ''
END
FROM sys.columns
WHERE object_id = OBJECT_ID('dbo.' + #tablename)
FOR XML PATH('')
), 1, 3, ''
) +
''')' + CASE WHEN #idInsert = 1 THEN CHAR(13) + #setIdentity + ' OFF' ELSE '' END +
''' FROM ' + #tablename + ' WHERE ' + #idField + ' = ' + CAST(#id AS VARCHAR)
INSERT #results EXEC (#execsql)
DELETE #excludedFields
SET #cnt = #cnt + 1
END
DECLARE cur CURSOR FOR SELECT Sql FROM #results
OPEN cur
DECLARE #sql NVARCHAR(4000)
FETCH NEXT FROM cur INTO #sql
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #sql
FETCH NEXT FROM cur INTO #sql
END
CLOSE cur
DEALLOCATE cur
You can Choose 'Result to File' option in SSMS and export your select result to file and make your changes in result file and finally using BCP - Bulk copy you can insert in table 1 in database 2.
I think for bulk insert you have to convert .rpt file to .csv file
Hope it will help.
I had a similar problem, but I needed to be able to create an INSERT statement from a query (with filters etc.)
So I created following procedure:
CREATE PROCEDURE dbo.ConvertQueryToInsert (#input NVARCHAR(max), #target NVARCHAR(max)) AS BEGIN
DECLARE #fields NVARCHAR(max);
DECLARE #select NVARCHAR(max);
-- Get the defintion from sys.columns and assemble a string with the fields/transformations for the dynamic query
SELECT
#fields = COALESCE(#fields + ', ', '') + '[' + name +']',
#select = COALESCE(#select + ', ', '') + ''''''' + ISNULL(CAST([' + name + '] AS NVARCHAR(max)), ''NULL'')+'''''''
FROM tempdb.sys.columns
WHERE [object_id] = OBJECT_ID(N'tempdb..'+#input);
-- Run the a dynamic query with the fields from #select into a new temp table
CREATE TABLE #ConvertQueryToInsertTemp (strings nvarchar(max))
DECLARE #stmt NVARCHAR(max) = 'INSERT INTO #ConvertQueryToInsertTemp SELECT '''+ #select + ''' AS [strings] FROM '+#input
exec sp_executesql #stmt
-- Output the final insert statement
SELECT 'INSERT INTO ' + #target + ' (' + #fields + ') VALUES (' + REPLACE(strings, '''NULL''', 'NULL') +')' FROM #ConvertQueryToInsertTemp
-- Clean up temp tables
DROP TABLE #ConvertQueryToInsertTemp
SET #stmt = 'DROP TABLE ' + #input
exec sp_executesql #stmt
END
You can then use it by writing the output of your query into a temp table and running the procedure:
-- Example table
CREATE TABLE Dummy (Id INT, Comment NVARCHAR(50), TimeStamp DATETIME)
INSERT INTO Dummy VALUES (1 , 'Foo', GetDate()), (2, 'Bar', GetDate()), (3, 'Foo Bar', GetDate())
-- Run query and procedure
SELECT * INTO #TempTableForConvert FROM Dummy WHERE Id < 3
EXEC dbo.ConvertQueryToInsert '#TempTableForConvert', 'dbo.Dummy'
Note:
This procedure only casts the values to a string which can cause the data to look a bit different. With DATETIME for example the seconds will be lost.
I created the following procedure:
if object_id('tool.create_insert', 'P') is null
begin
exec('create procedure tool.create_insert as');
end;
go
alter procedure tool.create_insert(#schema varchar(200) = 'dbo',
#table varchar(200),
#where varchar(max) = null,
#top int = null,
#insert varchar(max) output)
as
begin
declare #insert_fields varchar(max),
#select varchar(max),
#error varchar(500),
#query varchar(max);
declare #values table(description varchar(max));
set nocount on;
-- Get columns
select #insert_fields = isnull(#insert_fields + ', ', '') + c.name,
#select = case type_name(c.system_type_id)
when 'varchar' then isnull(#select + ' + '', '' + ', '') + ' isnull('''''''' + cast(' + c.name + ' as varchar) + '''''''', ''null'')'
when 'datetime' then isnull(#select + ' + '', '' + ', '') + ' isnull('''''''' + convert(varchar, ' + c.name + ', 121) + '''''''', ''null'')'
else isnull(#select + ' + '', '' + ', '') + 'isnull(cast(' + c.name + ' as varchar), ''null'')'
end
from sys.columns c with(nolock)
inner join sys.tables t with(nolock) on t.object_id = c.object_id
inner join sys.schemas s with(nolock) on s.schema_id = t.schema_id
where s.name = #schema
and t.name = #table;
-- If there's no columns...
if #insert_fields is null or #select is null
begin
set #error = 'There''s no ' + #schema + '.' + #table + ' inside the target database.';
raiserror(#error, 16, 1);
return;
end;
set #insert_fields = 'insert into ' + #schema + '.' + #table + '(' + #insert_fields + ')';
if isnull(#where, '') <> '' and charindex('where', ltrim(rtrim(#where))) < 1
begin
set #where = 'where ' + #where;
end
else
begin
set #where = '';
end;
set #query = 'select ' + isnull('top(' + cast(#top as varchar) + ')', '') + #select + ' from ' + #schema + '.' + #table + ' with (nolock) ' + #where;
insert into #values(description)
exec(#query);
set #insert = isnull(#insert + char(10), '') + '--' + upper(#schema + '.' + #table);
select #insert = #insert + char(10) + #insert_fields + char(10) + 'values(' + v.description + ');' + char(10) + 'go' + char(10)
from #values v
where isnull(v.description, '') <> '';
end;
go
Then you can use it that way:
declare #insert varchar(max),
#part varchar(max),
#start int,
#end int;
set #start = 1;
exec tool.create_insert #schema = 'dbo',
#table = 'customer',
#where = 'id = 1',
#insert = #insert output;
-- Print one line to avoid the maximum 8000 characters problem
while len(#insert) > 0
begin
set #end = charindex(char(10), #insert);
if #end = 0
begin
set #end = len(#insert) + 1;
end;
print substring(#insert, #start, #end - 1);
set #insert = substring(#insert, #end + 1, len(#insert) - #end + 1);
end;
The output would be something like that:
--DBO.CUSTOMER
insert into dbo.customer(id, name, type)
values(1, 'CUSTOMER NAME', 'F');
go
If you just want to get a range of rows, use the #top parameter as bellow:
declare #insert varchar(max),
#part varchar(max),
#start int,
#end int;
set #start = 1;
exec tool.create_insert #schema = 'dbo',
#table = 'customer',
#top = 100,
#insert = #insert output;
-- Print one line to avoid the maximum 8000 characters problem
while len(#insert) > 0
begin
set #end = charindex(char(10), #insert);
if #end = 0
begin
set #end = len(#insert) + 1;
end;
print substring(#insert, #start, #end - 1);
set #insert = substring(#insert, #end + 1, len(#insert) - #end + 1);
end;
You can Use Sql Server Integration Service Packages specifically designed for Import and Export operation.
VS has a package for developing these packages if your fully install Sql Server.
Integration Services in Business Intelligence Development Studio
I think its also possible with adhoc queries
you can export result to excel file and then import that file into your datatable object or use it as it is and then import the excel file into the second database
have a look at this link
this can help u alot.
http://vscontrols.blogspot.com/2010/09/import-and-export-excel-to-sql-server.html
If you are using Oracle (or configure the application to the SQL Server) then Oracle SQL Developer does this for you. choose 'unload' for a table and follow the options through (untick DDL if you don't want all the table create stuff).
I found this SMSMS Boost addon, which is free and does exactly this among other things. You can right click on the results and select Script data as.
You can use this Q2C.SSMSPlugin, which is free and open source. You can right click and select "Execute Query To Command... -> Query To Insert...". Enjoy)
You can use an INSERT INTO SELECT statement, to insert the results of a select query into a table. http://www.w3schools.com/sql/sql_insert_into_select.asp
Example:
INSERT INTO Customers (CustomerName, Country)
SELECT SupplierName, Country
FROM Suppliers
WHERE Country='Germany'

Remove trailing empty space in a field content

I am using SQL server MSDE 2000. I have a field called notes of type nvarchar(65).
The content is 'Something ' with an extra space after the content (quotes for clarity) in all the records. I used the following command.
UPDATE TABLE1
SET notes = RTRIM(LTRIM(notes))
But it does not work. Is there any alternate way to do it?
Are you sure the query isn't working? Try:
SELECT TOP 100 '~'+ t.notes +'~'
FROM TABLE1 t
TOP 100 will limit the results to the first 100 rows, enough to get an idea if there's really a space in the output. If there is, and RTRIM/LTRIM is not removing it - then you aren't dealing with a whitespace character. In that case, try:
UPDATE TABLE1
SET notes = REPLACE(notes,
SUBSTRING(notes, PATINDEX('%[^a-zA-Z0-9 '''''']%', notes), 1),
'')
WHERE PATINDEX('%[^a-zA-Z0-9 '''''']%', notes) <> 0
... OR you could literally just copy/paste the blank ' ' (space) at the end of a field as a result of your query into your replace statement and update everything from there.
update TABLE1
set notes = replace(notes, ' ', '')
And just in case you need to TRIM all spaces in all columns, you can use this script to do it dynamically:
--Just change table name
declare #MyTable varchar(100)
set #MyTable = 'MyTable'
--temp table to get column names and a row id
select column_name, ROW_NUMBER() OVER(ORDER BY column_name) as id into #tempcols from INFORMATION_SCHEMA.COLUMNS
WHERE DATA_TYPE IN ('varchar', 'nvarchar') and TABLE_NAME = #MyTable
declare #tri int
select #tri = count(*) from #tempcols
declare #i int
select #i = 0
declare #trimmer nvarchar(max)
declare #comma varchar(1)
set #comma = ', '
--Build Update query
select #trimmer = 'UPDATE [dbo].[' + #MyTable + '] SET '
WHILE #i <= #tri
BEGIN
IF (#i = #tri)
BEGIN
set #comma = ''
END
SELECT #trimmer = #trimmer + CHAR(10)+ '[' + COLUMN_NAME + '] = LTRIM(RTRIM([' + COLUMN_NAME + ']))'+#comma
FROM #tempcols
where id = #i
select #i = #i+1
END
--execute the entire query
EXEC sp_executesql #trimmer
drop table #tempcols