Selecting column name dynamically in an insert query - sql

Getting error Invalid column name '#ColumnNames'. in the last line (insert clause), any idea why ?
Declare #ColumnNames varchar(2000)
Declare #OrderId int
set #OrderId = 110077
select #ColumnNames = COALESCE(#ColumnNames + ', ', '') + COLUMN_NAME
from
INFORMATION_SCHEMA.COLUMNS
where
TABLE_NAME='OrderItems'
Insert into dbo.OrderHistory(#ColumnNames) select * from dbo.[Order] where ID= #OrderId

#ColumnNames is a string of text, not a list of columns. As a result, when you try to use it as a list of column names in the insert query, it fails.
You can use dynamic SQL to do what you desire, like so:
declare #insertquery nvarchar(1000)
set #insertquery = N'insert into dbo.orderhistory(' + #ColumnNames + ') select * from dbo.[Order] where ID=' + cast(#OrderId as nvarchar(10))
sp_executesql #insertquery

You should use dynamic sql. And dont forget to perform data casting constructing query string!
Declare #ColumnNames varchar(2000)
Declare #OrderId int
set #OrderId = 110077
select #ColumnNames = COALESCE(#ColumnNames + ', ', '') + COLUMN_NAME
from
INFORMATION_SCHEMA.COLUMNS
where
TABLE_NAME='OrderItems'
Declare #DynSqlStatement varchar(max);
set #DynSqlStatement = 'Insert into dbo.OrderHistory('+ #ColumnNames + ')
select * from dbo.[Order] where ID= ' + cast(#OrderId as varchar(10));
exec( #DynSqlStatement );

if you have to select all column from order table then there is no need to define #columnname

Related

map a string list in sqlserver like listagg

i try conver some string such as '1,2,3' to 'a,b,c' with the anwser:
select stuff(
(
select ',' + realname from sys_user
where ','+'1,2,3'+',' like '%,'+cast(u_id as varchar(10))+',%' for xml path('')
),1,1,'')
charindex is well done. but i want to create a more common function, so that i can convert in any relation such that.
i try a function :
create function [dbo].[fn_enum2str]
(
#enum as varchar(1000),
#table_name as varchar(100),
#origin_field as varchar(100),
#target_field as varchar(100)
)
as
begin
declare #result varchar(1000)
declare #sqlstr nvarchar(1000)
set #sqlstr = 'set #result = ('
set #sqlstr = #sqlstr + 'select stuff('
set #sqlstr = #sqlstr + '(select '','' + ' +#target_field+ ' from ' + #table_name
set #sqlstr = #sqlstr + ' where '','+#enum+','' like ''%,''+cast('+#origin_field+' as varchar)+'',%'' for xml path(''''))'
set #sqlstr = #sqlstr + ',1,1,''''))'
exec(#sqlstr)
return #result
end
it faild with error, as you know, it is not allow to exec a dynamic sql in function.
i want to
select dbo.fn_enum2str(a.uids,'sys_user','u_id', 'realname') from my_table a
--output 'a,b,c'
so, in my question, how can i create a function or a proc to deal it ?
Suppose you have SQL-SERVER2016 you can use string_split like this:
Test data
CREATE TABLE [dbo].[stringlist]([Numbers] [nvarchar](50) NULL)
Insert into dbo.Stringlist(numbers)
values('1,2,3,4,5,10')
SQL Function
alter function dbo.HinkyBase26( #Value as varchar(250) ) returns VarChar(250) as
begin
--declare #Value as varchar(50) = '13,14,1,2,5,14'
-- Notes: 'A' = 0. Negative numbers are not handled.
declare #Result as VarChar(250) = '';
declare #stringsplit table (numbers nvarchar(50),Letters varchar(1))
insert into #stringsplit(numbers,Letters)
select numbers = #Value ,CHAR(64 + value) as Letters from string_split(#Value,',')
select #Result = Letter from (
select numbers,Letter = STUFF((Select ', ' + Letters
from #stringsplit b
where b.numbers = a.numbers
FOR XML PATH('')),1,2,'')
from #stringsplit a
group by numbers
)z
return #Result
end
Execution of function
SELECT TOP (1000) [Numbers],dbo.HinkyBase26(Numbers)
FROM [LegOgSpass].[dbo].[stringlist]
SQL Stored Proc
Create PROC dbo.usp_convertnumberstostring
#stringvalue nvarchar(250)
AS
BEGIN
Create table #stringsplit (numbers nvarchar(50),Letters varchar(1))
insert into #stringsplit(numbers,Letters)
SELECT Numbers = #stringvalue,CHAR(64 + value) as Letters
from string_split(#stringvalue,',')
select numbers,Letter = STUFF((Select DISTINCT ', ' + Letters
from #stringsplit b
where b.numbers = a.numbers
FOR XML PATH('')),1,2,'')
from #stringsplit a
group by numbers
drop table #stringsplit
END
Execute SP
DECLARE #RC int
DECLARE #stringvalue nvarchar(250) = '1,5,6'
-- TODO: Set parameter values here.
EXECUTE #RC = [dbo].[usp_convertnumberstostring]
#stringvalue
GO
Result
SQL Script
Create table #stringsplit (numbers nvarchar(50),Letters varchar(1))
insert into #stringsplit(numbers,Letters)
SELECT Numbers,CHAR(64 + value) as Letters
FROM [LegOgSpass].[dbo].[stringlist] a
cross apply string_split(numbers,',')
select numbers,Letter = STUFF((Select DISTINCT ', ' + Letters
from #stringsplit b
where b.numbers = a.numbers
FOR XML PATH('')),1,2,'')
from #stringsplit a
group by numbers
Drop table #stringsplit
CREATE function [dbo].[fn_enum2str]
(
#enum as varchar(1000),
#table_name as varchar(100)
)
returns varchar(1000)
as
begin
declare #result varchar(1000)
if #enum is null
return ''
if #table_name = 'sys_user'
set #result = (
select stuff(
(
select ',' + realname from sys_user
where ','+#enum+',' like '%,'+cast(u_id as varchar(10))+',%' for xml path('')
),1,1,''
)
)
if #table_name = 'sys_attachment'
set #result = (
select stuff(
(
select ',/' + filepath from sys_attachment
where ','+#enum+',' like '%,'+cast(aid as varchar(10))+',%' for xml path('')
),1,1,''
)
)
return #result
end
GO
only way to deal it what i can think of, to switch which sql will be exec by a flag. when other relation apearance, add it to the switch list.
select
dbo.fn_enum2str(a.uids, 'sys_user') as names,
dbo.fn_enum2str(a.attachids, 'sys_attachment') as filepaths
from my_table a
so that it can be overlay. yes, it is difficult to remember stuff or for xml path or listagg(oracle), and result to a long sql, and i am lazy.😄
if you have any anwser better, tell me, thanks.

SQL Server : how to insert using variable

I am trying to insert data into a SQL Server table using a variable. I tried
DECLARE #table NVARCHAR(50) = 'ToolList',
#val NVARCHAR(50) = 'test'
EXEC ('INSERT INTO ' + #table + 'SELECT ' + #val)
and
EXEC ('INSERT INTO ' + #table + '([col1]) VALUES(' + #val +')'
but still get an error that says
Incorrect syntax near 'test'.
you missed a space before SELECT and the #val should enclosed in single quote
DECLARE #table nvarchar(50) = 'ToolList',
#val nvarchar(50) = 'test'
EXEC ( 'INSERT INTO ' + #table + ' SELECT ''' + #val + '''')
when you use Dynamic SQL, it is easier to form the query in a variable so that you can print out , inspect the value before execution
select #sql = 'INSERT INTO ' + #table + ' SELECT ''' + #val + ''''
print #sql
exec (#sql)
You'd better use sp_executesql that allows for statements to be parameterized, to avoid the risk of SQL injection.
DECLARE #Query NVARCHAR(1000),
#table NVARCHAR(50) = 'ToolList'
SET #Query = 'INSERT INTO ' + #table + ' SELECT #val'
EXEC sp_executesql #Query, N'#val nvarchar(50)', #val = 'test'
sp-executesql-transact-sql
You can also use CHAR(39) instead of adding single quotes every time for better readability. And also, you have not added a space after the variable which contains the table name.
Query
declare #table nvarchar(50) = 'ToolList',
#val nvarchar(50) = 'test2';
declare #sql as varchar(max) = 'insert into ' + #table
+ ' select ' + char(39) + #val + char(39);
exec(#sql);
You need 4 singlequotes before the #val field as it is a string and all strings needs to be encapsulated in single quotes.
You can print the dynamic string using PRINT command check what the final string you are going to execute.
DECLARE #table VARCHAR(50) = 'ToolList'
DECLARE #val VARCHAR(50) = 'test'
DECLARE #DSQL AS VARCHAR(MAX) = ''
SET #DSQL = #DSQL + ' INSERT INTO [' + #table + ']' + '
SELECT ' + '''' + #val + ''''
--PRINT #DSQL
EXEC(#DSQL)

Select table and column dynamically based on other table rows

I have following table and values,
Tb_name column_name1 column_name2
Citator_KTLO_CC Date_Created Date_Modified
Citator_KTLO_QA Date_Created Date_Modified
I want to select dynamically column from table, so the result is like this:
Select Date_Created,Date_Modified from Citator_KTLO_CC
and in next loop it will select for second row, like
Select Date_Created,Date_Modified from Citator_KTLO_QA
How can i do this by using dynamic sql ?
any example are appreciated.
here is an example of how to do this.
Since you dont post many info I just assume that the table containing all the tablenames is called 'tables'
Also this will only work if all tables have the same column types.
-- create a test table you dont need this
create table tables (tb_name varchar(100) primary key, field1 varchar(100), field2 varchar(100))
-- fill my test table you dont need this
insert into tables values ('table1', 'field1', 'field2')
insert into tables values ('table2', 'foo1', 'foo2')
insert into tables values ('table3', 'test1', 'test2')
-- this is the actual code you need, replace the names with your real names
declare #sql varchar(max) = ''
declare #tb_name varchar(100) = ''
declare #field1 varchar(100) = ''
declare #field2 varchar(100) = ''
declare myCursor cursor for
select tb_name, field1, field2 from tables -- dont know how your table is called
open myCursor
fetch next from myCursor into #tb_name, #field1, #field2
while ##FETCH_STATUS = 0
begin
set #sql = #sql + ' select ' + #field1 + ', ' + #field2 + ' from ' + #tb_name + ' union all '
fetch next from myCursor into #tb_name, #field1, #field2
end
close myCursor
deallocate myCursor
select #sql = left(#sql, len(#sql) - 10)
exec (#sql)
EDIT:
using a where clause is possible but things will get more complicated
declare #something date = getdate()
set #sql = #sql + ' select ' + #field1 + ', ' + #field2 + ' from ' + #tb_name + ' where ' + #field1 + ' = ' + #something + ' union all '
You can use the example above to build what you need just play with it.
EDIT:
using a where clause with a date format
declare #something date = getdate()
set #sql = #sql + ' select ' + #field1 + ', ' + #field2 + ' from ' + #tb_name + ' where ' + #field1 + ' = ''' + CONVERT(varchar(8), #something, 112) + ''' union all '
DECLARE #SQL VARCHAR(1000);
SET #SQL = '
SELECT *
FROM Citator_KTLO_CC
UNION ALL
SELECT *
FROM Citator_KTLO_QA;'
EXEC (#SQL);
How about something like this. If you've more than two cols, you can use dynamic sql to generate a list of cols to then generate more dynamic sql instead of hard coding.
DROP TABLE #Test
CREATE TABLE #Test
(Tb_name NVARCHAR(15),
column_name1 NVARCHAR(12),
column_name2 NVARCHAR(13));
INSERT INTO #Test VALUES
('Citator_KTLO_CC','Date_Created','Date_Modified'),
('Citator_KTLO_QA','Date_Created','Date_Modified');
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = (SELECT STUFF((SELECT ' UNION ALL SELECT ' + Cols + ' FROM '+TbL
FROM (SELECT QUOTENAME(Tb_name) TBL,
QUOTENAME(column_name1) + ', '+
QUOTENAME(column_name2) Cols
FROM #Test) Blah
FOR XML PATH('')),1,10,''))
PRINT #SQL
EXEC sys.sp_executesql #SQL
Try this..
For selecting one row if you are running in aloop
DECLARE #sql NVARCHAR(4000)
SELECT #sql = ' select ' + column_name_1 + ',' + column_name2 + ' from ' + Tb_name
FROM < yourtable >
EXEC (#sql)
OR
DECLARE #sql NVARCHAR(4000)
SELECT #sql = 'union all select ' + column_name_1 + ',' + column_name2 + ' from ' + Tb_name
FROM < yourtable >
SET #sql =stuff(#sql,1,10,'')
EXEC (#sql)
DECLARE #ColumnList1 VARCHAR(MAX) = '''''';
DECLARE #ColumnList2 VARCHAR(MAX) = '''''';
DECLARE #ColumnNameFromTable1 VARCHAR(50);
DECLARE #ColumnNameFromTable2 VARCHAR(50);
DECLARE MyCursor1 CURSOR
FOR
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Citator_KTLO_CC'
ORDER BY ORDINAL_POSITION
DECLARE MyCursor2 CURSOR
FOR
SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Citator_KTLO_QA'
ORDER BY ORDINAL_POSITION
OPEN MyCursor1
OPEN MyCursor2
FETCH NEXT FROM MyCursor1 INTO #ColumnNameFromTable1;
WHILE ##FETCH_STATUS = 0
BEGIN
FETCH NEXT FROM MyCursor2 INTO #ColumnNameFromTable2;
SET #ColumnList1 = #ColumnList1 + ',' + #ColumnNameFromTable1
SET #ColumnList2 = #ColumnList2 + ',' + #ColumnNameFromTable2
FETCH NEXT FROM MyCursor1 INTO #ColumnNameFromTable1;
END
CLOSE MyCursor1;
DEALLOCATE MyCursor1;
CLOSE MyCursor2;
DEALLOCATE MyCursor2;
EXEC ('SELECT ' + #ColumnList1 + ' FROM Citator_KTLO_CC UNION ALL SELECT ' +
#ColumnList2 + ' FROM Citator_KTLO_QA ')

Create table as value from select statement

How to create table from select statement?
For example, I have format table as below:
FormatID Label
1 ID
2 Name
3 DOB
So I want to create new table with column name ID, Name, DOB.
Any pointer would be appreciated.
You could try like this:
-- build the SQL query
declare #sql nvarchar(max) = ''
select #sql = #sql + '[' + Label + '] nvarchar(255), ' from Format order by FormatID
select #sql = 'create table [MyTable] (' + #sql + ')'
-- create the table
EXECUTE sp_executesql #sql
go
-- lets see if the table actually got created
sp_help MyTable
go
you can do ...
declare #sql as varchar( max) ='';
declare #cln as varchar( max) ='';
select #cln =( SELECT Label + ' nvarchar(50) , ' from format FOR XML PATH('')
);
set #sql = 'create table tablename ( '+ #cln + ' );
sp_executesql #sql ;
#sandeep rawat. Thanks, I modified some and i can now got it ;)
declare #sql as nvarchar(max) ='';
declare #cln as nvarchar(max) ='';
select #cln =(SELECT REPLACE(label, ' ', '') + ' nvarchar(50),' from format FOR XML PATH('') );
set #cln = substring(#cln,1,len(#cln)-1)
set #sql = 'create table new_table ('+ #cln + ')';
print #sql
print len(#sql)
exec sp_executesql #sql ;
This will help you
select FormatID,Label into New_table_Name from Table_Name where 1=0;
There was little misunderstanding.
Hope it will help you.
create table #Tbl_Format
(FormatId int identity(1,1),
Lebel Varchar(64)
)
insert into #Tbl_Format
values('Id'),('Name'),('DOB')
Declare #Query nvarchar(512)
SET #Query= (SELECT ', ' + Lebel+' NVARCHAR(64)'
FROM #Tbl_Format
FOR XML PATH(''))
SET #Query='CREATE TABLE Table_Name ('+Substring(#Query,2,LEN(#Query))+')'
EXEC (#Query)
Thanks

Must declare the scalar variable with Table-Valued Parameters and Stored Procedure

Could someone explain why the following gives me "Must declare the scalar variable #facilities." but works fine if I were to use VARCHAR instead of my created type? How can I fix this?
TIA
--CREATE TYPE integer_list AS TABLE (n int NOT NULL PRIMARY KEY)
--DROP PROCEDURE spTestTVP
CREATE PROCEDURE spTestTVP (
#facilities integer_list READONLY
--#facilities varchar(100)
)
AS
DECLARE #sql nvarchar(4000)
SET #sql = 'SELECT * FROM TestTable'
SET #sql = #sql + ' WHERE 1=1 '
IF #facilities IS NOT NULL
SET #sql = #sql + ' AND (FacilityNo IN (' + #facilities + ') OR FacilityNo IS NULL)'
EXEC sp_executesql #sql
DECLARE #items VARCHAR(MAX)
SELECT#items = COALESCE(#items+',' ,'') + n
FROM #facilities
DECLARE #sql nvarchar(4000)
SET #sql = 'SELECT * FROM TestTable'
SET #sql = #sql + ' WHERE 1=1 '
IF #facilities IS NOT NULL
SET #sql = #sql + ' AND (FacilityNo IN (' + #items + ') OR FacilityNo IS NULL)'
EXEC sp_executesql #sql
or not dynamic as follows:
SELECT t.* FROM TestTable as t
left outer join
#facilities as f
on
f.n = t.FacilityNo
where
t.FacilityNo is null
or
f.n is not null
When you are concatenating things in a dynamicSQl statment all pieces that build the statement must be either varchar or nvarchar. That is a limit of SQL.
On the other hand you don't need dynamic sql since you have created a table.
SELECT * FROM TestTable t
LEFT join #facilities f on f.n = t.facilityNO