Dynamically create column in Table - sql

I have Create a table within My procedure.
I have start date and end date value. I want create columns with this date range like if start date is 15 july 2013 and end date is 18 july 2013 then there are 4 columns(july1,july2,july3,july4) are created.
How can I achieve this. I have used sql server 2008..
please help me.....
Sir from front end i have fromdate and Todate fields then on submit I have called a Sp from my C# code, Now I want to create a table within my Proc. table must have no. of columns as with no. of days exists between given dates.

Try this:
DECLARE #dtmin date
DECLARE #dtmax date
DECLARE #dynsql nvarchar(3000)
DECLARE #colname varchar(20)
SET #dynsql = N'CREATE TABLE trial('
SET #dtmin = {d '2013-07-15'}
SET #dtmax = {d '2013-07-18'}
DECLARE #currdate date
SET #currdate = #dtmin
WHILE (#currdate <= #dtmax)
BEGIN
SET #colname = DATENAME(month, #currdate) + CONVERT(varchar, DATEPART(day, #currdate))
SET #dynsql = #dynsql + #colname + ' varchar(10)'
IF (#currdate < #dtmax)
BEGIN
SET #dynsql = #dynsql + ', '
END
ELSE
BEGIN
SET #dynsql = #dynsql + ')'
END
SET #currdate = DATEADD(day, 1, #currdate)
END
EXEC sp_executesql #dynsql
P.S.: I don't know the reason of your request, but generally is not correct create table in this way because the information you want to put into columns, should be put in the rows.
Example:
If I want to store sales day by day, the correct table, I named SALES, is:
SALES (id varchar(36), dt datetime, amount decimal(19,2))
instead of
SALES (ID VARCHAR(36), dt1 decimal(19,2), dt2 decimal(19,2)... and so on)
Because if you want to change your period you must rewrite your table, if you want query your table you must write several queries for each situation. It's very hard work on this table.
Please, reconsider you choice.
Have a nice day.

You may have accepted the answer above, but even wanting to do this goes well against the principles behind relational databases. I'd seriously consider what you're trying to do and your approach.

Related

Generating filed name with concat

My table has column names m1,m2,m3...,m12.
I'm using iterator to select them and insert them one by one in another table.
In this iterator I'm trying to generate filed names with:
'['+concat('m',cast(#P_MONTH as nvarchar))+']'
where #P_MONTH is incrementing in each loop.
so for #P_MONTH = 1 this suppose to give [m1] which works fine.
But when I run query I get:
Conversion failed when converting the nvarchar value '[m1]' to data
type int.
And if I put simply [m1] in that select it works ok.
How to concat filed name so it can be actually interpreted as filed name from certain table?
EDIT
Here is full query:
DECLARE #SQLString nvarchar(500),
#P_YEAR int,
#P_MONTH int = 1
set #P_YEAR = 2018
WHILE #P_MONTH < 13
BEGIN
SET #SQLString =
'INSERT INTO [dbo].[MASTER_TABLE]
(sector,serial,
date, number, source)'+
'SELECT ' + '[SECTOR],[DEPARTMENT]' +
QUOTENAME(cast(CONVERT(datetime,CONVERT(VARCHAR(4),#P_YEAR)+RIGHT('0'+CONVERT(VARCHAR(2),#P_MONTH),2)+'01',5) as nvarchar))+
QUOTENAME ('M',cast(#P_MONTH as nvarchar)) +
'EMPLOYED' +
'FROM [dbo].[STATS]'+
'where YEAR= #P_YEAR'
EXECUTE sp_executesql #SQLString
SET #P_MONTH = #P_MONTH + 1
END
It's still not working. It executes successfully but it does nothing.
Good day,
Let's create a simple table for the sake of the explanation
DROP TABLE IF EXISTS T
GO
CREATE TABLE T(a1 INT)
GO
INSERT T(a1) VALUES (1),(2)
GO
SELECT a1 FROM T
GO
When we are using a query like bellow, the server parse the text as a value and not as a column name
DECLARE #String NVARCHAR(10)
SELECT #String = '1'
--
SELECT '['+concat('a',cast(#String as nvarchar))+']'
FROM T
GO
This mean that the result will be 2 rows with no name for the column and the value will be "[a1]"
Moreover, the above query uses the brackets as part of the string.
One simple solution is to use the function QUOTENAME in order to add brackets around a name.
Another issue in this approach is the optional risk of SQL Injection. QUOTENAME might not be perfect solution but can help in this as well.
If we need to use entities name dynamically like in this case the column name then for most cases using dynamic query is the best solution. This mean to use the Stored Procedure sp_executesql as bellow
DECLARE #String INT
SELECT #String = 1
DECLARE #SQLString nvarchar(500);
SET #SQLString =
'SELECT ' + QUOTENAME(concat('a',cast(#String as nvarchar))) + ' FROM T'
EXECUTE sp_executesql #SQLString
GO

How to create/add columns using a variable in a loop

I am very new to SQL in that I just finished reading Sams Teach Yourself SQL in 10 Minutes and that is my only SQL knowledge. So now that I'm done with the book, I'm trying to create some tables so I can play around with them. I can easily create a table with a known amount of columns and specified header. Where I am having trouble is creating a table with an unknown amount of columns and a date as the header. What I have tried so far is this:
DECLARE #start_date AS DATE
DECLARE #end_date AS DATE
DECLARE #curr_date AS DATE
DECLARE #column_name AS CHAR(10)
SET #start_date = 2016-01-02
SET #end_date = 2016-12-31
SET #curr_date = #start_date
WHILE #curr_date < #end_date
SET #curr_date = DATEADD(DD, 7, #curr_date)
ALTER TABLE Project_1
ADD #curr_date DOUBLE
What I tried to do here is make a start and end point for the loop and use the loop condition which is stored in a local variable as my column header since that is what I need the column to be titled. I also tried using CAST to cast it as a char but the DBMS is delighted to let me know that there is Incorrect syntax near '#curr_date' on that last line (the ADD line) because it doesn't like that I'm trying to name a column with a local variable (I think). I don't have a sample output but the output should be a table with the first column defined as CHAR and titled emp_name because it will be holding names. All other columns defined as type DOUBLE and should be NULL because they will be holding a number of hours and must have the current date as the header #curr_date. I think all columns added to a table through the ALTER method are defaulted to NULL anyways. I've seen examples of dynamic SQL where you declare a variable to hold the select statement but I don't really understand how they add columns to a table. I'm not sure if this can be done in the CREATE statement but if it can that would be great to see. Also, this needs to be variable in the fact that I could change the #end_date to lets say... they year 2046.
Security is not an issue here
I agree with all of the comments about using ROWs not dynamically adding columns you can always dynamically pivot those latter and it will have more flexibily. So maybe some schema considerations but just to answer your specific question you where close.....
DECLARE #start_date AS DATE
DECLARE #end_date AS DATE
DECLARE #curr_date AS DATE
DECLARE #column_name AS CHAR(10)
SET #start_date = '2016-01-02'
SET #end_date = '2016-12-31'
SET #curr_date = #start_date
WHILE #curr_date < #end_date
BEGIN
DECLARE #SQL NVARCHAR(MAX)
SET #curr_date = DATEADD(DD, 7, #curr_date)
SET #SQL = 'ALTER TABLE TableB
ADD [' + CAST(#curr_date AS VARCHAR(10)) + '] FLOAT'
--PRINT #SQL
EXECUTE (#SQL)
END

stored procedure concatenate two parameters in where clause

I've got an annoying problem, where I pass two parameters into a stored procedure and join them together for use within the WHERE clause of my statement. The first parameter is used in an equals expression, whereas the second is to form an AND clause.
The below extract of the stored proc, shows what I am attempting to do.
Declare #CombinedWhereClause varchar(500), #Sender varchar(10), #AndClause char(200)
Set #Sender = 'Wayne';
Set #AndClause = ' AND Convert(varchar(8), MessageDate, 112) < DATEADD(day, -10, GETDATE())';
Set #CombinedWhereClause = #Sender + #AndClause;
SELECT Messages.Id, Messages.IdExternal, Messages.MessageReference,
FROM Messages
WHERE Messages.Sender =#CombinedWhereClause
If I replace the #CombinedWhereClause with #Sender only then I get back the expected records. If I change the where clause to be:
WHERE Message.MessageDate Convert(varchar(8), MessageDate,112) < DATEADD(day, -10, GETDATE())
then I get all records which are 10 days old. However, when combined I get no results.
In your statement are several flaws:
The comma behind the MessageReference
You try to have WHERE Messages.sender=Wayne but you need quotes ...='Wayne'
You think the db engine is somehow magic :-)
This is - for sure! - not the approach one should chose...
Try this dynamic approach (SQL Server syntax)
Declare #CombinedWhereClause varchar(500), #Sender varchar(10), #AndClause char(200)
Set #Sender = '''Wayne'''; --you need to wrap the word in quotes!
Set #AndClause = ' AND Convert(varchar(8), MessageDate, 112) < DATEADD(day, -10, GETDATE())';
Set #CombinedWhereClause = #Sender + #AndClause;
DECLARE #cmd VARCHAR(MAX)=
'SELECT Messages.Id, Messages.IdExternal, Messages.MessageReference
FROM Messages
WHERE Messages.Sender =' + #CombinedWhereClause;
--check the command
SELECT #cmd;
--if the command is OK you might execute it
--EXEC(#cmd);

Is there anyway to dynamically add column in a SQL Server table based on current year?

I had a SQL Server table with columns:
target_2011, target_2012, target_2013, target_2014, target_2015
Is there any way to add a dynamic column target_2016 to the table as soon as we enter into the year 2016, based on the current year?
Create a SQL agent job with
DECLARE #column varchar(20), #SQL varchar(1000)
SET #column = 'target_'+CAST(year(getdate()) AS varchar(4))
SET #SQL =
'ALTER TABLE <yourtable>
ADD '+#column+' varchar(50)'
EXECUTE (#SQL)
Now schedule this job to run at 00:01 on the 1st of January every year.
While this will do what you are looking for, I agree with the observations in the comment section. This is an inherently bad design and not a good idea at all.

Pass Datetime in SQL query in string format?

I have string query in and pass #Date object to string. It is giving error. See below code.
Declare #MidDate datetime, #MaxDate datetime
set #qrysales_trans_unit_26wks ='update historical_result
set sales_trans_unit_26wks = (
SELECT
SUM(sales_trans_unit)
FROM reg_summary_rowno WHERE
period_idx >= '+ #MidDate // error
+' AND period_idx <'+ #MaxDate /error
+' AND Client_id ='+ #Client_id
+' and historical_result.[store_idx] = reg_summary_rowno.[store_idx]
And [attributes] ='+ #attributes +')'
How to pass Datetime object in the proper way to string Query?
Try using two single quotes to escape quote marks so dates end up like: period_idx >= '#MidDate'
set #qrysales_trans_unit_26wks ='update historical_result
set sales_trans_unit_26wks = (
SELECT
SUM(sales_trans_unit)
FROM reg_summary_rowno WHERE
period_idx >= '''+ #MidDate
+''' AND period_idx <'''+ #MaxDate
+''' AND Client_id ='+ #Client_id
+' and historical_result.[store_idx] = reg_summary_rowno.[store_idx]
And [attributes] ='+ #attributes +')'
Click here for more information on escaping quotes in SQL.
A couple of better options, IMHO.
If you really want to use dynamic SQL, read up on sp_executesql - and use the ability to pass in parameters to the SQL. You'll prevent SQL injection attacks this way and will also avoid running into problems with having to string-ify parameter values.
Otherwise, used stored procedures - which I would consider the better option here.
To fix your ERROR, you need to add some single quotes ' around the dates within the string.
One more thing which improves clarity. Use the BETWEEN keyword:
WHERE period_idx BETWEEN #MinimumDate AND #MaximumDate
You can use instead of datetime a smalldatetime
And you may use the dates like this :
Declare #MidDate smalldatetime,
set #MidDate = '20110317'
Hope it helps.
If you must pass a date in string format - first of all, put it in quotes, and second of all, I would strongly urge you to use the standard ISO-8601 date format (YYYYMMDD or YYYY-MM-DDTHH:MM:SS).
The big benefit of these ISO standard formats is that they'll work no matter what language and regional settings your SQL Server is set to. Any other string representation is language-dependent, e.g.
05/10/2010
will mean:
10th of May 2010 in the US
5th of October 2010 in pretty much all of the rest of the world
but 20101005 is clear and never ambiguous - it's always the 5th of October, 2010 - even for in the US :-)
I think you should use convert before concatenate the date variable with the sentence
Declare #MidDate datetime, #MaxDate datetime
set #qrysales_trans_unit_26wks = 'update historical_result
set sales_trans_unit_26wks = (
SELECT
SUM(sales_trans_unit)
FROM reg_summary_rowno
WHERE
period_idx >= '+ '''' + convert(varchar, #MidDate, 112) + '''' // error
+' AND period_idx <'+ '''' + convert(varchar, #MaxDate, 112) + '''' /error
+' AND Client_id ='+ #Client_id
+' and historical_result.[store_idx] = reg_summary_rowno.[store_idx]
And [attributes] ='+ #attributes +')'
I would really recommend that you shy from concatenating SQL this way. It will really open you to injection attacks etc.
Look at this sample to see another approach that you might take.
use tempdb
create table foo (id int not null identity, data datetime)
insert foo(data) values
('1/1/2010'),('1/10/2010'),('3/31/2010')
Declare #SQLStr nvarchar(4000)
set #SQLStr = 'select * from foo where data = #Data'
exec sp_executeSQL #SQLStr, N'#Data datetime', '1/1/2010'