Related
I want someway to automate table creations as every day customer can add some columns ,remove some ,so my idea is to pass table name and columns into a table then use this table in stored procedure to automatically creates the table.
This is table that will hold tables structure
create table nada_test
(
table_name varchar(500),
col_name varchar(100),
col_type varchar(100)
)
Sample data:
insert into nada_test
values ('data', 'salary', 'int'), ('data', 'id', 'int'),
('data', 'job', 'varchar(100)')
Could someone show me how to achieve this?
How about that
CREATE TABLE T
(
TableName varchar(500),
ColName varchar(100),
ColType varchar(100)
);
INSERT INTO T VALUES
('data','salary','int'),
('data', 'id', 'int'),
('data', 'job', 'varchar(100)');
DECLARE #SQL NVARCHAR(MAX);
SELECT #SQL = N'CREATE TABLE Data ('+ STUFF((
SELECT ',' + ColName + ' ' + ColType
FROM T
FOR XML PATH('')
), 1, 1, '') + N' );'
FROM T;
SELECT #SQL [CreateTable];
--EXECUTE sp_executesql #SQL;
But that won't help you
What will happen to the data already exists in your table?
What if the table already exists, ok you can pass that by IF OBJECT_ID() .., but still, what will happen to the data already in your table?
You will face another problem even if you store the data in temp table because the structure of both tables is not the same even the datatypes of the columns.
As it already been mentioned, your approach is very vulnerable to SQL injections.
See example:
insert into #nada_test
values ('TestTable] (TestColumn int);SELECT * FROM sys.tables--', 'TestColumn', 'INT')
GO
DECLARE #TableName sysname, #ColumnName sysname, #Type VARCHAR(100), #SQL VARCHAR(2000)
WHILE EXISTS (SELECT TOP 1 1 FROM #nada_test)
BEGIN
SELECT TOP 1 #TableName = table_name, #ColumnName = [col_name], #Type = col_type FROM #nada_test
DELETE FROM #nada_test WHERE #TableName = table_name and #ColumnName = [col_name]
IF NOT EXISTS ( SELECT TOP 1 1 FROM sys.tables WHERE name = #TableName)
SET #SQL = 'CREATE TABLE [' + #TableName + '] ([' + #ColumnName + '] ' + #Type + ');'
ELSE IF NOT EXISTS ( SELECT TOP 1 1 FROM sys.columns WHERE name = #ColumnName AND object_id = OBJECT_ID(#TableName))
SET #SQL = 'ALTER TABLE [' + #TableName + '] ADD [' + #ColumnName + '] ' + #Type + ';'
ELSE
SET #SQL = 'PRINT ''TABLE name [' + #TableName + '] with column [' + #ColumnName + '] is already exists'';'
PRINT #SQL
EXEC (#SQL)
END
Generally we can use like
create table x as select * from y ;
using some existing table structure say y in this case
You can create a ddl trigger on your existing requirement i.e. if theres any change to this table
then fire the same query above.
Please help me:
I have 100 columns in a table & i want to select 99 columns but i don't want to write 99 columns. Is there any way.
Thanks in advance,
Nishant Khanna
In MSSQL:
declare #schema varchar(128)
declare #table varchar(128)
declare #exceptedColumn varchar(128)
declare #sql nvarchar(max)
set #schema = 'dbo'
set #table = 'MyTable'
set #exceptedColumn = 'DontWantThat'
set #sql = 'SELECT ' + (
select STUFF((select ',' + '[' + columns.name + ']' from sys.columns
join sys.tables on tables.object_id = columns.object_id
join sys.schemas on schemas.schema_id = tables.schema_id
where schemas.name = #schema and tables.name = #table and columns.name <> #exceptedColumn
for xml path('')), 1, 1, '')
) + ' FROM [' + #schema + '].[' + #table + ']'
exec sp_executesql #sql
Can you try this?
select * into [#my_table] from [my_table];
alter table [#my_table] drop column [my_column];
select * from [#my_table];
Replace the **TableName** with yours and Column name that you don't want in your query.
This will surely work
SELECT left(replace((
SELECT COLUMN_NAME + ','
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = '**TableName**'
FOR XML path('')
), '**Column_Name_That_You_Don't want in your query**' + ',', ''), (
len(replace((
SELECT COLUMN_NAME + ','
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = '**TableName**'
FOR XML path('')
), '**Column_Name_That_You_Don't want in your query**' + ',', ''))
) - 1)
Create a view with the 99 columns and use that in your code instead of the actual table.
I have a table say Configs in which I have the column names
id | Name
-------------
1 | Fruits
2 | Vegetables
3 | Pulses
I have another table say Details
id |Field1 | Field2| Field3 | Active | Mandatory
-------------------------------------------------
1 |Apple |Potato |Red gram | 1 |0
2 |Mango |Peas |Chick Peas| 0 |0
I need field1, field2, field3 to be selected as the name of 1st table
eg.
select
id,
Field1 as Fruits,
Field2 as Vegetables,
Field3 as pulses,
Active,
Mandatory
From
Details
How do I do it?
declare #sql nvarchar(max)
select #sql = isnull(#sql + ',' ,'')
+ N'Field' + convert(varchar(10), id) + ' as ' + quotename(Name)
from Config
-- Form the dynamic SQL
select #sql = 'SELECT id,'
+ #sql
+ ',Active, Mandatory '
+ 'FROM Details'
-- Print to verify
print #sql
-- Execute it
exec sp_executesql #sql
Something like these perhaps:
Method #1
IF OBJECT_ID(N'Configs', N'U') IS NULL
CREATE TABLE Configs(ID INT, Name NVARCHAR(25));
ELSE
TRUNCATE TABLE Configs
INSERT INTO Configs VALUES(1, 'Fruits'), (2, 'Vegetables'), (3, 'Pulses');
IF OBJECT_ID(N'Configs', N'U') IS NULL
CREATE TABLE Details(ID INT, Field1 NVARCHAR(25), Field2 NVARCHAR(25), Field3 NVARCHAR(25)
,Active BIT, Mandatory BIT);
ELSE
TRUNCATE TABLE Details
INSERT INTO Details VALUES(1, 'Apples', 'Potato', 'Red Gram', 1, 0)
,(2, 'Mango', 'Peas', 'Chick Peas', 0, 0);
DECLARE #Sql NVARCHAR(MAX);
SELECT #Sql = 'SELECT id,' + CHAR(13)
+ ' [Field1] as ' + QUOTENAME((SELECT Name FROM Configs WHERE ID = 1)) +',' + CHAR(13)
+ ' [Field2] as ' + QUOTENAME((SELECT Name FROM Configs WHERE ID = 2)) +',' + CHAR(13)
+ ' [Field3] as ' + QUOTENAME((SELECT Name FROM Configs WHERE ID = 3)) +',' + CHAR(13)
+ ' Active,' + CHAR(13)
+ ' Mandatory ' + CHAR(13)
+ 'From Details' + CHAR(13);
PRINT #Sql;
EXEC sp_executesql #Sql;
Method #2
IF OBJECT_ID(N'Configs', N'U') IS NULL
CREATE TABLE Configs(ID INT, Name NVARCHAR(25));
ELSE
TRUNCATE TABLE Configs
INSERT INTO Configs VALUES(1, 'Fruits'), (2, 'Vegetables'), (3, 'Pulses');
IF OBJECT_ID(N'Configs', N'U') IS NULL
CREATE TABLE Details(ID INT, Field1 NVARCHAR(25), Field2 NVARCHAR(25), Field3 NVARCHAR(25)
,Active BIT, Mandatory BIT);
ELSE
TRUNCATE TABLE Details
INSERT INTO Details VALUES(1, 'Apples', 'Potato', 'Red Gram', 1, 0)
,(2, 'Mango', 'Peas', 'Chick Peas', 0, 0);
DECLARE #Sql NVARCHAR(MAX) = 'SELECT' + CHAR(13) + CHAR(9) + 'ID' + CHAR(13);
SELECT
#Sql += STUFF((
SELECT
CHAR(9) + ',' + QUOTENAME('Field' + LTRIM(STR(ROW_NUMBER()OVER(ORDER BY ID)))) + ' AS ' + QUOTENAME(Name) + CHAR(13)
FROM
Configs
FOR XML PATH(''), type).value('.', 'nvarchar(max)'), 1, 0, '');
SELECT
#Sql += CHAR(9) +',Active' + CHAR(13)
+ CHAR(9) +',Mandatory ' + CHAR(13)
+ 'From Details' + CHAR(13);
PRINT #Sql;
EXEC sp_executesql #Sql;
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
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;