Related
I have a table with records which has 100 columns, I need to get the count of distinct values of all the columns from this table based on some condition (where clause).
Below query is working fine, but I'm not able to use the where clause. So it's giving the result for all the records of the table. But I want it to be based on some condition lets say column file_id = 1;. My question is how to use where clause with the below query. Or if there is any other alternative way to solve this problem.
declare #SQL nvarchar(max)
set #SQL = ''
;with cols as (
select Table_Schema, Table_Name, Column_Name, Row_Number() over(partition by Table_Schema, Table_Name
order by ORDINAL_POSITION) as RowNum
from INFORMATION_SCHEMA.COLUMNS
)
select #SQL = #SQL + case when RowNum = 1 then '' else ' union all ' end
+ ' select ''' + Column_Name + ''' as Column_Name, count(distinct ' + quotename (Column_Name) + ' ) As DistinctCountValue,
count( '+ quotename (Column_Name) + ') as CountValue FROM ' + quotename (Table_Schema) + '.' + quotename (Table_Name)
from cols
where Table_Name = 'table_name' --print #SQL
execute (#SQL)
I am using the dynamic query because I need to reuse this query for other tables also.
First get the columns and use stuff to generate the select in this way:
SELECT COUNT(ColumnA) AS ColumnA, COUNT(ColumnB AS ColumnB), COUNT(ColumnC) AS ColumnC....
That way you only select on your table once to get all counts, After that, use CROSS APPLY to "unpivot" those columns and return the output on one row per column
CROSS APPLY(
VALUES(1, 'ColumnA', ColumnA), (2, 'ColumnB', ColumnB), (3, 'ColumnC', ColumnC)
)(ID, ColumnName, DistinctCountValue)
For the filter, use sp_executesql and send the file_id as parameter
exec SP_executesql #SQL, N'#FID INT', #FID = #FileID
Since you are using all columns of the table Row_Number() over(partition by Table_Schema, Table_Name order by ORDINAL_POSITION) as RowNum becomes redundant, ORDINAL_POSITION already has the value that you are looking for
declare #tablename nvarchar(50) = 'MyTestTable'
declare #fileID int = 1
declare #SQL nvarchar(max)
set #SQL = ''
;with cols as (
select TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, ORDINAL_POSITION
from INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = #TableName
)
select #SQL = ';WITH CTE AS (SELECT
' +
STUFF((
SELECT ', COUNT(DISTINCT ' + QUOTENAME(COLUMN_NAME) + ') AS ' + QUOTENAME(COLUMN_NAME)
FROM cols
ORDER BY ORDINAL_POSITION
FOR XML PATH('')
), 1, 1, '')
+ '
FROM ' + #TableName + '
WHERE File_ID = #FID
)
SELECT B.*
FROM CTE
CROSS APPLY (
VALUES ' +STUFF((
SELECT ',( ' + CAST(ORDINAL_POSITION AS VARCHAR) + ',' + QUOTENAME(COLUMN_NAME,'''') + ',' + QUOTENAME(COLUMN_NAME) + ')'
FROM cols
ORDER BY ORDINAL_POSITION
FOR XML PATH('')
), 1, 1, '') + '
)B (ID,ColumnName,DistinctCountValue)
'
from cols
exec SP_executesql #SQL, N'#FID INT', #FID = #FileID
The query below creates a table of all the column names and uses a while loop to select the count for whatever WHERE clause you want to use. This should be pretty flexible for any table; just update the top variables. Note that this will not count a column where its value is null. You can add a case to the #Query parameter if that's what you want. Since it processes each row individually, I added in a temp table so you only hit the db once.
IF OBJECT_ID('tempdb..##SourceValues') IS NOT NULL
DROP TABLE ##SourceValues
DECLARE #Schema VARCHAR(50) = 'SomeSchema'
DECLARE #Table VARCHAR(50) = 'SomeTable'
DECLARE #WhereClause VARCHAR(MAX) = ' Some WHERE clause'
DECLARE #ColumnName VARCHAR(50)
DECLARE #ProcessedRows TABLE(ColumnName VARCHAR(50), DistinctCount INT)
DECLARE #Columns TABLE(RowNumber INT, ColumnName VARCHAR(100))
INSERT INTO #Columns SELECT ROW_NUMBER() OVER(ORDER BY COLUMN_NAME DESC), COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #Table
DECLARE #Count INT = (SELECT MAX(RowNumber) FROM #Columns)
DECLARE #Counter INT = 0
DECLARE #DistinctCount INT
DECLARE #Query NVARCHAR(MAX)
EXEC('SELECT * INTO ##SourceValues FROM ' + #Table +' (NOLOCK)')
WHILE #Counter < #Count
BEGIN
SET #Counter += 1
SET #ColumnName = (SELECT ColumnName FROM #Columns WHERE RowNumber = #Counter)
SET #Query = 'SELECT #OutPut = COUNT(' + #ColumnName + ') FROM ' + #Schema + '.' + ' ##SourceValues ' + #WhereClause
EXECUTE sp_executesql #Query, N'#Output INT OUT', #DistinctCount OUT
INSERT INTO #ProcessedRows(ColumnName, DistinctCount) VALUES (#ColumnName, #DistinctCount)
END
SELECT * FROM #ProcessedRows
Let's try some different approach.
Get all values unpivoted as Param/Value:
1) Collect list of tables and columns to be used in dynamic SQL:
DROP TABLE IF EXISTS #Base;
;WITH SchemaData AS (
SELECT t.name AS [TableName],c.name AS [ColumnName],c.column_id AS [ColumnOrderID]
FROM sys.tables t
INNER JOIN sys.columns c ON c.object_id = t.object_id
)
SELECT t.TableName
,STUFF((SELECT ',CONVERT(NVARCHAR(MAX),' + QUOTENAME([ColumnName]) + ') AS ' + QUOTENAME([ColumnName])
FROM SchemaData a WHERE (a.TableName = t.TableName) FOR XML PATH(''),TYPE).value('(./text())[1]','NVARCHAR(MAX)'),1,1,'') AS [SelectClause]
,STUFF((SELECT ',' + QUOTENAME([ColumnName]) FROM SchemaData a WHERE (a.TableName = t.TableName) FOR XML PATH(''),TYPE).value('(./text())[1]','NVARCHAR(MAX)'),1,1,'') AS [UnpivotClause]
INTO #Base
FROM SchemaData t
GROUP BY t.TableName
;
2) Get all data inside a temp table
DROP TABLE IF EXISTS #Result;
CREATE TABLE #Result(TableName NVARCHAR(255),ColumnName NVARCHAR(255),[Value] NVARCHAR(MAX));
DECLARE #TableName NVARCHAR(255),#SelectClause NVARCHAR(MAX),#UnpivotClause NVARCHAR(MAX);
DECLARE crPopulateResult CURSOR LOCAL FAST_FORWARD READ_ONLY FOR SELECT b.TableName,b.SelectClause,b.UnpivotClause FROM #Base b;
OPEN crPopulateResult;
FETCH NEXT FROM crPopulateResult INTO #TableName,#SelectClause,#UnpivotClause;
DECLARE #dSql NVARCHAR(MAX);
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #dSql = N' INSERT INTO #Result(TableName,[ColumnName],[Value])
SELECT up.TableName,up.Param AS [ColumnName],up.[Value]
FROM (
SELECT ''' + #TableName + N''' AS [TableName]
,' + #SelectClause + N'
FROM ' + QUOTENAME(#TableName) + N'
) a
UNPIVOT(Value FOR Param IN (' + #UnpivotClause + N')) up
';
EXEC sp_executesql #stmt = #dSql;
FETCH NEXT FROM crPopulateResult INTO #TableName,#SelectClause,#UnpivotClause;
END
CLOSE crPopulateResult;
DEALLOCATE crPopulateResult;
3) Any filters can be applied with #Results, including Table names, column names, data filters, etc:
SELECT r.TableName,r.ColumnName,COUNT(*) AS [CountValue],COUNT(DISTINCT r.[Value]) AS [DistinctCountValue]
FROM #Result r
--
--WHERE r.ColumnName = 'file_id' AND r.[Value] = '1'
--
GROUP BY r.TableName,r.ColumnName
ORDER BY r.TableName,r.ColumnName
;
To use this with a where clause with this query you just have to put the where clause in the construction after the table name so if you wanted to filter on file_id='1' then you would have:
FROM ' + quotename (Table_Schema) + '.' + quotename (Table_Name) +'where file_id =''1'' '
You can add a #where variable and concatenate that with your big union construction (as part of your select ... from cols). For example:
declare #SQL nvarchar(max)
declare #where nvarchar(max) = ' where file_id = 1'
set #SQL = ''
;with cols as (
select Table_Schema, Table_Name, Column_Name, Row_Number() over(partition by Table_Schema, Table_Name
order by ORDINAL_POSITION) as RowNum
from INFORMATION_SCHEMA.COLUMNS
)
select #SQL = #SQL + case when RowNum = 1 then '' else ' union all ' end
+ ' select ''' + Column_Name + ''' as Column_Name, count(distinct ' + quotename (Column_Name) + ' ) As DistinctCountValue,
count( '+ quotename (Column_Name) + ') as CountValue FROM ' + quotename (Table_Schema) + '.' + quotename (Table_Name)
+ #where
from cols
where Table_Name = 'table_name' --print #SQL
execute (#SQL)
Note that you'll need to escape single quotes in #where if you're searching for a string. For example, declare #where nvarchar(max) = ' where state = ''CT'''.
I have a table named a. Some cells containing a string 'Empty' in many columns. I want to find this columns. Can you help me?.
Try this dynamic query, it will check all the columns with character data and list the columns which has the word 'Empty'.
DECLARE #SearchText VARCHAR(50) = 'Empty'
DECLARE #sql NVARCHAR(MAX) = 'SELECT '
SELECT #sql = #sql + 'MAX(CASE WHEN ' + c.COLUMN_NAME + ' LIKE ''%'+ #SearchText +'%'' THEN ''' + c.COLUMN_NAME +''' ELSE '''' END) + '','' + '
FROM INFORMATION_SCHEMA.COLUMNS c WHERE c.TABLE_SCHEMA = 'dbo' and c.TABLE_NAME = 'a'
AND c.DATA_TYPE IN ('varchar','char','nvarchar','nchar','sysname')
SET #sql = #sql + ''''' FROM dbo.a'
EXEC sys.sp_executesql #sql
Hope this helps
Use the LIKE operator:
SELECT a.*
FROM a
WHERE a.col1 LIKE '%Empty%' OR a.col2 LIKE '%Empty%' OR ...
In sql server you can get object id of table then using that object id you can fetch columns. In that case it will be as below:
Step 1: First get Object Id of table
select * from sys.tables order by name
Step 2: Now get columns of your table and search in it:
select * from a where 'Empty' in (select name from sys.columns where object_id =1977058079)
Note: object_id is what you get fetch in first step for you relevant table
You can do it using unpivot with an help of dynamic query , here i have done below an working sample for you , there might be some modification you might have to do to put the below psedo code with your working .
Sample table structure been used :
create table ColTest
(
name1 varchar(10),
name2 varchar(10),
name3 varchar(10),
name4 varchar(10)
)
insert into ColTest values ('sdas','asdasda','ewrewr','erefds')
insert into ColTest values ('sdas','asdasda','EMPTY','erefds')
insert into ColTest values ('EMPTY','asdasda','ewrewr','erefds')
DECLARE #table_name SYSNAME
SELECT #table_name = 'ColTest'
DECLARE #tmpTable SYSNAME
SELECT #tmpTable = 'ColTest2'
DECLARE #SQL NVARCHAR(MAX)
SELECT #SQL = '
SELECT * into
' + #tmpTable + '
FROM ' + #table_name + '
UNPIVOT (
cell_value FOR column_name IN (
' + STUFF((
SELECT ', [' + c.name + ']'
FROM sys.columns c WITH(NOLOCK)
LEFT JOIN (
SELECT i.[object_id], i.column_id
FROM sys.index_columns i WITH(NOLOCK)
WHERE i.index_id = 1
) i ON c.[object_id] = i.[object_id] AND c.column_id = i.column_id
WHERE c.[object_id] = OBJECT_ID(#table_name)
AND i.[object_id] IS NULL
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '') + '
)
) unpiv'
PRINT #SQL
EXEC sys.sp_executesql #SQL
select * from ColTest2 where cell_value = 'EMPTY'
I'd suggest dynamic SQL
--First you set the variable #TableName to your actual table's name.
DECLARE #TableName VARCHAR(100)='a';
--The following statement will create a list of all columns with a data type containing the word "char" (others should not hold the value Empty)
DECLARE #ColList VARCHAR(MAX)=
STUFF(
(
SELECT ' OR ' + COLUMN_NAME + ' LIKE ''%empty%'''
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME=#TableName AND DATA_TYPE LIKE '%char%'
FOR XML PATH('')
),1,4,'');
--This statement builds a command
DECLARE #cmd VARCHAR(MAX)=
(
SELECT 'SELECT * FROM [' + #TableName + '] WHERE ' + #ColList
);
--Here you can see the command
PRINT #cmd;
--And here it is executed
EXEC(#cmd);
I am using the following SQL to list all table and column names in my schema for tables containing columns whose names contain the string "code" using the following SQL server query:
SELECT
a.table_name, a.column_name from (SELECT t.name AS table_name,
SCHEMA_NAME(schema_id) AS schema_name,
c.name AS column_name
FROM
sys.tables AS t
INNER JOIN
sys.columns c ON t.OBJECT_ID = c.OBJECT_ID
WHERE
c.name LIKE '%code%') a
Result:
Table Name Column Name
---------- -----------
Tab_1_name a_code
Tab_2_name another_code
Tab_3_name yet_another_code
and so on...
I would like to now query the actual data in the a_code and another_code columns using a wrapper but cannot see how to get at the actual data (if doing for Tab 1 individually for example, I would
SELECT a_code FROM Tab_1
to get
a_code
------
value 1
value 2
value 3
but can't figure out or find anywhere how to code the outer query to wrap around the above such that I would get something along the lines of:
Tab1_name a_code
--------- ------
tab_name 1 value 1
tab_name 1 value 2
tab_name 2 value 1
tab_name 2 value 2
tab_name 3 value 1
tab_name 3 value 2 ... etc.
i.e. a formatted list of all the data values in all table columns in my schema/DB whose names contain the word "code"?
Without dynamic SQL, this can't be done by anyway.
Here is something to get you started.
DECLARE #SearchTerm NVARCHAR(50)
SELECT #SearchTerm = '%id%'
SELECT t.name AS table_name,
SCHEMA_NAME(schema_id) AS schema_name,
c.name AS column_name
INTO #temp
FROM sys.tables AS t
INNER JOIN sys.columns c ON t.OBJECT_ID = c.OBJECT_ID
WHERE c.name LIKE #SearchTerm
ORDER BY t.name
DECLARE #Query NVARCHAR(MAX),
#tableName NVARCHAR(250),
#schemaName NVARCHAR(10),
#columnName NVARCHAR(250)
SELECT #Query = 'SELECT SchemaName = '''',
TableName = '''',
ColumnName = '''',
Value = CONVERT(NVARCHAR(MAX), '''')
WHERE 0 = 1'
WHILE(EXISTS(SELECT TOP 1 1 FROM #temp))
BEGIN
SELECT TOP 1 #tableName = table_name,
#schemaName = [schema_name],
#columnName = column_name
FROM #temp
SELECT #Query = #Query + ' UNION ALL SELECT SchemaName = ''' + #schemaName + ''',
TableName = ''' + #tableName + ''',
ColumnName = ''' + #columnName + ''',
Value = CASE WHEN ' + #columnName + ' IS NULL THEN ''NULL'' ELSE CONVERT(NVARCHAR(MAX), ' + #columnName + ') END
FROM ' + #tableName
DELETE #temp
WHERE table_name = #tableName
AND #schemaName = [schema_name]
AND #columnName = column_name
END
PRINT #Query
EXEC sp_executesql #Query
DROP TABLE #temp
The above query return the following information :
SchemaName TableName ColumnName Value
Beaware that by returning the value for all matching columns, you are very likely to encounter conversion problem and null conversion problem. In the query above, basic case are handled, but the conversion to 'NVARCHAR' might still fail with some complexes SQL column type.
use master
GO
declare
#sql varchar(max) = '',
#colpattern varchar(100) = '%name%'
;with cteSchema as
(
select
object_schema_name(t.object_id) + '.' + quotename(t.name) as tabname,
quotename(c.name) as colname
from sys.tables t
inner join sys.columns c on c.object_id = t.object_id
where c.name like #colpattern
)
select #sql =
(
select
cast('
select cast(t.' as varchar(max)) + t.colname + ' as varchar(1000)) as [value] '
+ ', cast(''' + t.tabname + '.' + t.colname + ''' as nvarchar(2000)) as [source] '
+ ' from ' + t.tabname + ' t
union all '
from cteSchema t
order by t.tabname, t.colname
for xml path(''), type
).value('.', 'varchar(max)')
+ '
select null, null where 1=0
order by [source], [value]'
print #sql
exec (#sql)
GO
How can I concatenate an arbitrary length of columns per row? I tried the following, but the function as is requires one to specify the column names:
SELECT CONCAT([C1], [C2], ...) FROM [dbo.table];
How can I achieve the same result without specifying each column name explicitly?
You'd need to use dynamic SQL. You can query the system catalg view sys.columns to get the column names, and then use SQL Server's XML Extension to concatenate the rows to a single string giving your final SQL to execute:
DECLARE #TableName SYSNAME = 'dbo.YourTable';
DECLARE #SQL NVARCHAR(MAX) = 'SELECT CONCAT(' +
STUFF(( SELECT ',' + QUOTENAME(c.Name)
FROM sys.columns c
WHERE [object_id] = OBJECT_ID(#TableName)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '') + ')
FROM ' + #TableName + ';';
EXECUTE sp_executesql #SQL;
ADDENDUM
If you want to delimit your columns, you can add a further concatenation while you are creating your column list:
DECLARE #TableName SYSNAME = 'dbo.YourTable',
#Delimiter VARCHAR(10) = ', ';
DECLARE #SQL NVARCHAR(MAX) = 'SELECT CONCAT(' +
STUFF(( SELECT ',''' + #Delimiter + ''',' + QUOTENAME(c.Name)
FROM sys.columns c
WHERE [object_id] = OBJECT_ID(#TableName)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, LEN(#Delimiter) + 5, '') + ')
FROM ' + #TableName + ';';
EXECUTE sp_executesql #SQL;
ADDENDUM 2
To avoid the delimiter being added when the value is null, e.g instead of ending up with:
1,,,2,3
You simply get
1,2,3
You need to slightly amend the logic, before it was generating a query that was like:
CONCAT([C1], ',', [C2], ',', [C3])
Instead you want:
CONCAT([C1], ',' + [C2], ',' + [C3])
Because you are now using ',' + [C2] if [C2] is null, the result will be null, so the delimiter will be removed:
DECLARE #TableName SYSNAME = 'dbo.YourTable',
#Delimiter VARCHAR(10) = ', ';
DECLARE #SQL NVARCHAR(MAX) = 'SELECT CONCAT(' +
STUFF(( SELECT ',''' + #Delimiter + ''' + ' + QUOTENAME(c.Name)
FROM sys.columns c
WHERE [object_id] = OBJECT_ID(#TableName)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, LEN(#Delimiter) + 7, '') + ')
FROM ' + #TableName + ';';
EXECUTE sp_executesql #SQL;
ADDENDUM 3
To remove the first column you can use ROW_NUMBER() on the sys.columns system catalog view, then exclude the first column:
DECLARE #TableName SYSNAME = 'dbo.YourTable',
#Delimiter VARCHAR(10) = ', ';
DECLARE #SQL NVARCHAR(MAX) = 'SELECT CONCAT(' +
STUFF(( SELECT ',''' + #Delimiter + ''' + ' + QUOTENAME(c.Name)
FROM ( SELECT name,
RowNumber = ROW_NUMBER() OVER(ORDER BY column_id)
FROM sys.columns c
WHERE [object_id] = OBJECT_ID(#TableName)
) AS c
WHERE c.RowNumber != 1 -- not first column
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, LEN(#Delimiter) + 7, '') + ')
FROM ' + #TableName + ';';
EXECUTE sp_executesql #SQL;
You need to use Dynamic-SQL for this:
Warning:
I've used tempdb (tempdb.sys.columns) because I cannot create normal tables in demo. In your case use your normal database. And change condition to: WHERE object_id = OBJECT_ID('table_name').
LiveDemo
CREATE TABLE #tab(ID INT, C1 INT, C2 INT, C3 INT);
INSERT INTO #tab VALUES (1, 1,2,3), (2, 2,3,4);
DECLARE #cols NVARCHAR(MAX);
SET #cols = STUFF(
(SELECT ',' + QUOTENAME(name)
FROM tempdb.sys.columns
WHERE
object_id = (SELECT object_id
FROM tempdb.sys.objects
WHERE NAME like '#tab%' AND Type = 'U')
AND name LIKE 'C%'
ORDER BY column_id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'');
DECLARE #query NVARCHAR(MAX)=
N'SELECT ID, CONCAT(<placeholder>) AS concated_columns FROM #tab';
SET #query = REPLACE(#query, '<placeholder>', #cols);
EXEC [dbo].[sp_executesql]
#query;
EDIT:
If you need specific character between concatenated values use:
(SELECT ',' + CONCAT(QUOTENAME(name) , ','' ''' )
LiveDemo2
To concat all columns in arbitrary table:
DECLARE #Columns nvarchar(MAX)
SELECT #Columns = ISNULL(#Columns + ',','') + COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'TableName' AND TABLE_SCHEMA='dbo'
DECLARE #sql nvarchar(MAX)
SET #sql = N'SELECT CONCAT('+#Columns+')
FROM dbo.TableName'
EXEC sp_executesql #sql
Use dynamic SQL and remember to cater for nulls!
declare #sql nvarchar(max), #t sysname, #c sysname
select #sql = 'select ', #t = '[dbo].[CONTACTS]' /* <---- YOUR TABLE NAME HERE */
declare cols cursor for
select name from sys.columns where object_id = object_id(#t) order by column_id
open cols
fetch next from cols INTO #c
while ##FETCH_STATUS = 0
begin
select #sql = #sql + 'convert(nvarchar(max), isnull(' + #c + ', '''')) + '
fetch next from cols INTO #c
end
close cols
deallocate cols
select #sql = left(#sql, len(#sql)-2) + ' from ' + #t
exec sp_executesql #sql
If your table has a primary key column, you could use a correlated subquery like the example below. SqlFiddle here.
SELECT (
( SELECT *
FROM dbo.table1 AS t2
WHERE t2.C1 = t1.C1 --specify primary key column(s) here
FOR
XML PATH('test')
, TYPE
)).value('/test[1]', 'nvarchar(MAX)') AS ConcatenatedValue
FROM dbo.table1 AS t1;
Does anybody know of a proc or script which will generate any row into an insert statement into the same table?
Basically, I'd like to call something like
exec RowToInsertStatement 'dbo.user', 45;
And the following code would be output
insert into dbo.MyTable( FirstName, LastName, Position)
values( 'John', 'MacIntyre', 'Software Consultant');
I realize I could
insert into dbo.MyTable
select * from dbo.MyTable where id=45;
But this obviously won't work, because the ID column will complain (I hope it complains) and there's no way to just override that one column without listing all columns, and in some tables there could be hundreds.
So, does anybody know of a proc that will write this simple insert for me?
EDIT 3:04: The purpose of this is so I can make a copy of the row, so after the INSERT is generated, I can modify it into something like
insert into dbo.MyTable( FirstName, LastName, Position)
values( 'Dave', 'Smith', 'Software Consultant');
.. no obviously this contrived example is so simple it doesn't make sense, but if you have a table with 60 columns, and all you need is to change 3 or 4 values, then it starts to be a hassle.
Does that make sense?
Update
I believe the following dynamic query is what you want:
declare #tableName varchar(100), #id int, #columns varchar(max), #pk varchar(20)
set #tableName = 'MyTable'
set #pk = 'id'
set #id = 45
set #columns = stuff((select ',['+c.name+']' [text()] from sys.tables t
join sys.columns c on t.object_id = c.object_id
where t.name = #tableName and c.name <> #pk for xml path('')),1,1,'')
print 'insert into [' + #tableName + '] (' + #columns + ')
select ' + #columns + '
from [' + #tableName + ']
where ' + #pk + ' = ' + cast(#id as varchar)
Update 2
The actual thing that you wanted:
declare #tableName varchar(100), #id int, #columns nvarchar(max), #pk nvarchar(20), #columnValues nvarchar(max)
set #tableName = 'MyTable'
set #pk = 'id'
set #id = 45
set #columns = stuff((select ',['+c.name+']' [text()] from sys.tables t
join sys.columns c on t.object_id = c.object_id
where t.name = #tableName and c.name <> #pk for xml path('')),1,1,'')
set #columnValues = 'set #actualColumnValues = (select' +
stuff((select ','','''''' + cast(['+c.name+'] as varchar(max)) + '''''''' [text()]' [text()]
from sys.tables t
join sys.columns c on t.object_id = c.object_id
where t.name = #tableName and c.name <> #pk for xml path('')),1,1,'')
+ 'from [' + #tableName + ']
where ' + #pk + ' = ' + cast(#id as varchar)
+ 'for xml path(''''))'
--select #columnValues
declare #actualColumnValues nvarchar(max), #columnValuesParams nvarchar(500)
SET #columnValuesParams = N'#actualColumnValues nvarchar(max) OUTPUT';
EXECUTE sp_executesql #columnValues, #columnValuesParams, #actualColumnValues OUTPUT;
--SELECT stuff(#actualColumnValues, 1,1, '')
declare #statement nvarchar(max)
set #statement =
'insert into [' + #tableName + '] (' + #columns + ')
select ' + stuff(#actualColumnValues,1,1,'')
print #statement
What it does is this:
It generates the insert statement and then it queries the actual data from the table and generates the select statement with that data. May not work correctly for some really complex datatypes but for varchars, datetimes and ints should work like a charm.
This stored proc works great for me:
http://vyaskn.tripod.com/code.htm#inserts
Did you know that in Enterprise Manager and SQL Server Management Studio that you can, from the object browser, drag the list of columns into the text window and it will drop the names of all the columns into the text, separated by commas?