简体   繁体   中英

CREATE TABLE with Dynamic Column

I need to insert some data into a temp table.

I have some conditional based columns like Salary , Code .

How can I create a table for conditional based columns? I don't want to use SELECT INTO #tempTable

Here is the code:

DECLARE @sql NVARCHAR(MAX)
,@sqlSelect NVARCHAR(MAX) = ''
,@sqlFrom NVARCHAR(MAX) =''

CREATE TABLE #myTempTable (Id INT, DeptId INT, DeptName VARCHAR(100))


SET @sqlSelect ='INSERT INTO #myTempTable
SELECT EMP.Id, EMP.DeptId, EMP.DeptName'

SET @sqlFrom =' FROM dbo.EMPLOYEE AS EMP'

IF (someCondition)
BEGIN
    SET @sqlSelect = @sqlSelect +', EMP.Salary, EMP.Code'            
END

SET @sql =  @sqlSelect +@sqlFrom 

EXEC sp_executesql @sql

Any help/suggestion on how better i can make this?


Update:

Initially I used SELECT INTO #TempTable without specifying no of columns, As SQL Azure not supporting that, I decided to go with INSERT INTO . But not sure how can I add dynamic columns in a defined structure already. Its fully dynamic SQL :(

How many dynamic columns do you need?

What about having a column called Dynamic of type nvarchar(MAX) or something of the like, and then you can just put your data in there and format as appropriate.

Another option is to create a table with NULL columns.

To do this in SQL, you can do the following:

CREATE TABLE tblPerson
(
    PersonId INT,
    FirstName NVARCHAR(256),
    LastName NVARCHAR(256) NULL,
    PRIMARY KEY (PersonId)
)

Notice the NULL column above also.

See here for further explanation on creating tables with Primary Keys and NULL columns in SQL:

Create tables in SQL

As alluded to, your best bet is to likely create all of the columns that you will need and make them NULLable if optional. So:

CREATE TABLE #myTempTable (Id INT, DeptId INT, DeptName VARCHAR(100), Salary INT NULL, EmpCode VARCHAR(45) NULL);

And

IF (someCondition)
BEGIN
    SET @sqlSelect = @sqlSelect +', EMP.Salary, EMP.Code'            
END
ELSE
BEGIN
    SET @sqlSelect = @sqlSelect +', NULL, NULL'            
END

See example below:

declare @sql nvarchar(400)
declare @ColFlag int
set @ColFlag = 1
if(@ColFlag = 0)

set  @sql= 'CREATE TABLE #myTempTable (Id INT, DeptId INT, DeptName VARCHAR(100)); 
    INSERT INTO #myTempTable SELECT 1,2,''DeptName1''; 
    SELECT * FROM #myTempTable'
else
set  @sql= 'CREATE TABLE #myTempTable (Id INT, DeptId INT, DeptName VARCHAR(100), MyNewColHere VARCHAR(25)); 
    INSERT INTO #myTempTable SELECT 1,2,''DeptName1'', ''MyNewColHereValue''; 
    SELECT * FROM #myTempTable'

 exec (@sql)

Below code may work for you.

DECLARE @sql NVARCHAR(MAX)
,@sqlSelect NVARCHAR(MAX) = ''

,@sqlFrom NVARCHAR(MAX) =''

IF (someCondition)
BEGIN
    CREATE TABLE #myTempTable (Id INT, DeptId INT, DeptName VARCHAR(100))    
END
ELSE
BEGIN
    CREATE TABLE #myTempTable (Id INT, DeptId INT, DeptName VARCHAR(100),Salary numeric(18,2), Code INT)    
END

SET @sqlSelect ='INSERT INTO #myTempTable
SELECT EMP.Id, EMP.DeptId, EMP.DeptName'

SET @sqlFrom =' FROM dbo.EMPLOYEE AS EMP'

IF (someCondition)
BEGIN
    SET @sqlSelect = @sqlSelect +', EMP.Salary, EMP.Code'            
END

SET @sql =  @sqlSelect +@sqlFrom 

EXEC sp_executesql @sql

You could just alter the temp table when the condition is met (I'm assuming you know the columns required at this point?)

IF (someCondition)
BEGIN
    ALTER TABLE #myTempTable ADD Salary VARCHAR(20), Code VARCHAR(20)
    SET @sqlSelect = @sqlSelect +', EMP.Salary, EMP.Code'            
END

You may want to test if the column exists first, if you have more than one condition that could be activated - which you can do like so:

IF COL_LENGTH('#myTemptable','Salary') IS NULL
BEGIN
     -- do something if salary doesn't exist
     ALTER TABLE #myTempTable ADD Salary NUMERIC(6,2)
END

As others have noted, you could create the columns upfront as nullable - or create the table after the condition is met.

Can you use the

sp_RENAME 'TableName.[OldColumnName]' , '[NewColumnName]', 'COLUMN' 

command to change the name of a previously defined place holder column.
For example, create the #tempTable with extra fields such as "VCHAR100_1" , "BOOL_1" , "Int_1" , etc. If you need to use the field, rename it to the desired name so your code later on can refer to the column by a non-cryptic name.

I think what you really are looking for is impossible for SQL. As I undrestand you want to add column for, for example tuples that met the conditions. So I think you need some other database engines like NoSQL(I'm not very familiar with NoSQL but as much as I know it can horizontally scale)

If you don't want to switch from sql I think you should reconsider your design.
If you insist on your design you can do things like for example add a column, name "column name" and another column "column data" and fill it when its needed. But as you can see it has its own constraints like the data of "column data" should be the same type.

use the code and tell me is it working or not ??

-----------------------------------------------------

DECLARE @sql NVARCHAR(MAX)
,@sqlSelect NVARCHAR(MAX) = ''

,@sqlFrom NVARCHAR(MAX) =''

IF (someCondition)
BEGIN
    CREATE TABLE #tblInfo (Id INT, DeptId INT, DeptName VARCHAR(100))    
END
ELSE
BEGIN
    CREATE TABLE #tblInfo (Id INT, DeptId INT, DeptName VARCHAR(100),Salary numeric(18,2), Code INT)    
END

SET @sqlSelect ='INSERT INTO #tblInfo 
SELECT EMP.Id, EMP.DeptId, EMP.DeptName'

SET @sqlFrom =' FROM dbo.EMPLOYEE AS EMP'

IF (someCondition)
BEGIN
    SET @sqlSelect = @sqlSelect +', EMP.Salary, EMP.Code'            
END

SET @sql =  @sqlSelect +@sqlFrom 

EXEC sp_executesql @sql

------------------------------------

I hope it will work

If this table is BIG I think your and in your logic you make JOINS with this table you need to create indexes in TARGET TABLE

You can go around with Physical table not TEMP

DECLARE @guid NVARCHAR(64), @sql NVARCHAR(MAX)
,@sqlSelect NVARCHAR(MAX) = ''
,@sqlFrom NVARCHAR(MAX) ='', @sqlSchema NVARCHAR(MAX) =''

SET @guid = NEWID()  

declare @tableName NVARCHAR(64)= 'myTempTable'+ @guid

select @tableName

--use some transaction

set @sql = 'CREATE TABLE ' + @tableName  
set @sqlSchema = '(Id INT, DeptId INT, DeptName VARCHAR(100)'  -- DO INDEXES IF YOU ARE JOIN 

SET @sqlSelect ='INSERT INTO ' + @tableName +
'SELECT EMP.Id, EMP.DeptId, EMP.DeptName'


IF (1=1) -- your condition
BEGIN
  SET @sqlSchema = @sqlSchema +', EMP.Salary, EMP.Code'            
END

SET @sqlSchema =   @sqlSchema + ')' -- close last )

set @sql =  @sql +@sqlSchema --create the TargetTable


--DO YOUR logic

EXEC sp_executesql @sql

set @sql = N'DROP TABLE ' + @tableName

EXEC sp_executesql @sql

Try This.

Begin Tran


IF (someCondition)

    Begin 
      CREATE TABLE #myTempTable (Id INT, DeptId INT, DeptName VARCHAR(100), Salary money , Code Int)

        Insert Into #myTempTable(Id, DeptId, DeptName, Salary, Code)
        Select Emp.Id, Emp.DeptID, Emp.DeptName, EMP.Salary, EMP.Code 
        From dbo.Employee As Emp

        Select Id, DeptID, DeptName, Salary, Code From dbo.Employee
              Drop Table #myTempTable     
    End

    Else

    Begin

      CREATE TABLE #myTempTable2 (Id INT, DeptId INT, DeptName VARCHAR(100))

        Insert Into #myTempTable(Id, DeptId, DeptName)
        Select Emp.Id, Emp.DeptID, Emp.DeptName 
        From dbo.Employee As Emp

        Select Id, DeptID, DeptName From dbo.Employee

                    Drop Table #myTempTable2 
    End



Commit Tran

Here is another solution with completely dynamic columns:

DECLARE @sql NVARCHAR(MAX)
,@sqlTableName NVARCHAR(MAX) = ''
,@sqlColumnsDefinitions NVARCHAR(MAX) =''
,@sqlColumnsList NVARCHAR(MAX) =''
,@sqlFrom NVARCHAR(MAX) =''

SET @sqlTableName = 'myTempTable'
SET @sqlFrom = 'FROM dbo.EMPLOYEE AS EMP'

--conditions to define columns
IF (1=1)
    set @sqlColumnsDefinitions='Col1 int, Col2 int, Col3 int'
else
    set @sqlColumnsDefinitions='Col1 int, Col2 int, Col3 int, Col4 int, Col5 int'

--new table with dynamic fields creation
SET @sql = 'if (object_id('''+@sqlTableName+''') is not null) DROP TABLE '+@sqlTableName+'
CREATE TABLE '+@sqlTableName+' ('+@sqlColumnsDefinitions+')'+char(10)
print (@sql)
exec (@sql)

--get columns list
select @sqlColumnsList=STUFF((SELECT ',' + column_name
from INFORMATION_SCHEMA.COLUMNS where table_name like @sqlTableName+'%' FOR XML PATH('')), 1, 1, '')

--main insert
SET @sql = 'INSERT INTO '+@sqlTableName+char(10)+'SELECT '+@sqlColumnsList+char(10)+@sqlFrom
print (@sql)
exec  (@sql)

This won't work if you substite myTempTable with #myTempTable because of SQL engine. To fix it and get correct column names list you may use some tricks that are out of scope. But still it works with non-temp tables.

You can generate ALTER statements which dynamically alter the structure of your table. Shown below with a temp table, but of course the approach also works for normal tables.

 IF OBJECT_ID( 'tempdb..#temp') IS NOT NULL
    BEGIN
   DROP TABLE #temp
   END
CREATE TABLE #temp
    (
    [CVRelID]       INT,
    [CustomerID]    INT,
    [VendorID]      INT,
    [TransactionID] INT
    )

DECLARE @script VARCHAR(8000)
SET     @script = 'ALTER TABLE #temp ADD '

SELECT  @script = @script + QUOTENAME(SDName) + ' VARCHAR(100),'
FROM    tblSupportDocuments
WHERE   SDIsActive = 1


SET     @script = SUBSTRING( @script, 1, LEN(@script) - 1)-- remove last ","
PRINT   @script
EXEC    (@script)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM