I want to create a dynamic table in SQL Server using dynamic Table name and dynamic Column name. For example:
Table name : 01-02-2015
Column names:
Id 01 02 03.... 28
When I creating a temp table is OK but I want to create a real Table then I use the following script like this, when execute the error occur:
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near '.01'.
Code:
DECLARE #DynamicSQL as NVARCHAR(max),#TempTableName as nvarchar(max)
DECLARE #TimeSheetDate as DateTime
DECLARE #startDate AS DATETIME --Cursor Local Variables
DECLARE #endDate AS DATETIME
SET #TimeSheetDate = '2015-2-15'
SET #startDate = DATEADD(mm, DATEDIFF(mm, 0, #TimeSheetDate), 0) -- the first day of month
SET #endDate = DATEADD (dd, -1, DATEADD(mm, DATEDIFF(mm, 0, #TimeSheetDate) + 1, 0))-- the last day of month
SET #TempTableName = #startDate -- the first day of month
SET #DynamicSQL='CREATE TABLE dbo.'+ quotename(#TempTableName, '[') + '(Id int identity(1,1) not null primary key);';
WHILE (#startDate <= #endDate)
BEGIN
--DECLARE #DynamicSQL VARCHAR(500)
BEGIN
SET #DynamicSQL = 'ALTER TABLE dbo.' + #TempTableName +
' ADD ['+ CONVERT(VARCHAR(2), #startDate, 105) + '] NVARCHAR(max) NULL'
EXECUTE (#DynamicSQL)
END
SET #startDate = DateADD(dd, 1, #startDate)
IF #startDate - 1 = #endDate
BREAK;
END
exec (#DynamicSQL);
If I use this script to create a temp table is OK:
DECLARE #TempTableName as nvarchar(100)
DECLARE #TimeSheetDate as DateTime
DECLARE #startDate AS DATETIME --Cursor Local Variables
DECLARE #endDate AS DATETIME
SET #TimeSheetDate = '2015-2-15'
SET #startDate = DATEADD(mm, DATEDIFF(mm, 0, #TimeSheetDate), 0)
SET #endDate = DATEADD (dd, -1, DATEADD(mm, DATEDIFF(mm, 0, #TimeSheetDate) + 1, 0))
SET #TempTableName = DATEADD(mm, DATEDIFF(mm, 0, #TimeSheetDate), 0)
if exists (select * from tempdb.dbo.sysobjects o where o.xtype in ('U') and o.id = object_id(N'tempdb..##TempTableName'))
DROP TABLE ##TempTableName
CREATE TABLE ##TempTableName(Id int identity(1,1) not null primary key) -- Creating Temp Table
-- Loop to add columns to temp table
WHILE (#startDate <=#endDate)
BEGIN
DECLARE #DynamicSQL VARCHAR(500)
BEGIN
SET #DynamicSQL = 'ALTER TABLE ##TempTableName ADD ['+ CONVERT(VARCHAR(2),#startDate,105) +'] NVARCHAR(100) NULL'
EXECUTE (#DynamicSQL)
END
SET #startDate = DateADD(dd,1,#startDate)
IF #startDate-1 = #endDate
BREAK;
END
SELECT * FROM ##TempTableName
The problem is in your alter table statement.
Change
SET #DynamicSQL = 'ALTER TABLE dbo.' + #TempTableName +
' ADD ['+ CONVERT(VARCHAR(2), #startDate, 105) + '] NVARCHAR(max) NULL'
To
SET #DynamicSQL = 'ALTER TABLE dbo.' + quotename(#TempTableName, '[') +
' ADD ['+ CONVERT(VARCHAR(2), #startDate, 105) + '] NVARCHAR(max) NULL'
This works for my expectation:
ALTER PROCEDURE [dbo].[proc_InsertTimeSheetInit]
#TimeSheetDate as DateTime
AS
BEGIN
DECLARE #DynamicSQL as NVARCHAR(255)
DECLARE #TableName as nvarchar(255)
DECLARE #startDate AS DATETIME
DECLARE #endDate AS DATETIME
DECLARE #dropSQL as NVARCHAR(255)
SET #startDate = DATEADD(mm, DATEDIFF(mm, 0, #TimeSheetDate), 0) -- the first day of month
SET #endDate = DATEADD (dd, -1, DATEADD(mm, DATEDIFF(mm, 0, #TimeSheetDate) + 1, 0))-- the last day of month
SET #TableName ='TS'+convert(nvarchar(2), datepart(mm, #TimeSheetDate)) + convert(nvarchar(4), datepart(yyyy, #TimeSheetDate))
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].['+ #TableName+ ']') AND type in (N'U'))
SELECT #dropSQL = 'DROP TABLE dbo.' + QUOTENAME(#TableName) + '';
exec (#dropSQL)
SET #DynamicSQL = 'create table [dbo].[' + #TableName + ']([Id] [int] IDENTITY(1,1) NOT NULL,CONSTRAINT PK_'+#TableName+' PRIMARY KEY CLUSTERED (Id))'
exec (#DynamicSQL)
WHILE (#startDate <= #endDate)
BEGIN
BEGIN
SET #DynamicSQL = 'ALTER TABLE [dbo].[' + #TableName + '] ADD ['+ CONVERT(VARCHAR(2), #startDate, 105) + '] NVARCHAR(50) NULL'
EXECUTE (#DynamicSQL)
END
SET #startDate = DateADD(dd, 1, #startDate)
IF #startDate - 1 = #endDate
BREAK;
END
END
Related
I have the following code, which works as expected.
DECLARE #startdate datetime2 = '2007-05-05';
DECLARE #enddate datetime2 = '2007-05-04';
SELECT DATEDIFF(year, #startdate, #enddate);
I now want to be able to use a variable to switch from "year, week, days, etc. So I tried this, but it didn't work. Any suggestions?
DECLARE #startdate datetime2 = '2007-05-05';
DECLARE #enddate datetime2 = '2007-05-04';
DECLARE #interval varchar = 'year'
SELECT DATEDIFF(#interval, #startdate, #enddate);
I get:
Msg 1023, Level 15, State 1, Line 25
Invalid parameter 1 specified for datediff.
Thank you,
Raul Gonzalez
You have to use dynamic sql for this. Once you start using dynamic sql you open yourself up to sql injection. Here is how you can do this and protect yourself against sql injection. The case expression may seem a little odd but if you have a value in your #interval that is not allowed the entire sql string will be NULL and no harm will come to your database. Also, please notice that you did not specify a length for #interval. For variables the default length is 1 so it would only be 'y' not 'year' as you expected it to be. Always specify the scale and precision of variables.
DECLARE #startdate datetime2 = '2007-05-05';
DECLARE #enddate datetime2 = '2007-05-04';
DECLARE #interval varchar(10) = 'year'
declare #SQL nvarchar(max)
set #SQL = 'select datediff(' +
case #interval when 'year' then 'year'
when 'week' then 'week'
when 'days' then 'day'
end
+ ', #startdate, #enddate)'
exec sp_executesql #SQL, N'#startdate datetime2, #enddate datetime2', #startdate, #enddate
You could do it dynamically:
declare #datediff varchar(max)
set #datediff = 'SELECT DATEDIFF(' + #interval + ',' + '''' + cast(#startdate as varchar(10)) + '''' + ',' + '''' + cast(#enddate as varchar(10)) + '''' + ')'
exec (#datediff)
Now let's say that I have the following:
DECLARE #startdate datetime2 = '2017-11-03';
DECLARE #enddate datetime2 = '2018-11-03';
DECLARE #interval varchar(10) = 'month'
declare #SQL nvarchar(max)
set #SQL = 'select datediff(' +
case #interval
when 'year' then 'year'
when 'month' then 'month'
when 'week' then 'week'
when 'day' then 'day'
end
+ ', #startdate, #enddate)'
exec sp_executesql #SQL, N'#startdate datetime2, #enddate datetime2',
#startdate, #enddate
The result is "12"
Now I'd like to have 12 values displayed as:
2017/11/03
2017/12/03
2018/01/03
2018/02/03
2018/03/03
2018/04/03
2018/05/03
2018/06/03
2018/07/03
2018/08/03
2018/09/03
2018/10/03
I have a stored procedure as shown below. When i use the command so as to call the stored procedure, i get the error message
Must declare the scalar variable "#MusNo"."
I would like to ask your help.
exec sp_executesql N' execute [isb].[SP_GetNakitIslemSorguList_Test] NULL,159986569,''2016/01/01 12:23:45'',''2016/03/03 21:10:12'' '
USE [ATMDB]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE proc [isb].[SP_GetNakitIslemSorguList_Test]
(
#IslemKodu as varchar(30),
#MusNo as bigint,
#StartDate as Date,
#EndDate as Date
)
AS
DECLARE #Sql nvarchar(max)
DECLARE #AddToWhereSql varchar(50)
IF #IslemKodu is null
BEGIN
SET #AddToWhereSql = '1=1'
END
ELSE
BEGIN
SET #AddToWhereSql = 'IslemKodu = #IslemKodu'
END
BEGIN
SET #Sql = N'SELECT [KartNo],[HesapNo],[TCKN],[CepTel],[MusNo],[Alacakli],[Tutar],[Tarih],[AtmNo],[Borclu]
FROM [ATMDB].[isb].[NakitIslemSorgu] WITH (NOLOCK)
WHERE MusNo = #MusNo and cast([Tarih] as Date)>= #StartDate and cast([Tarih] as Date) <= #EndDate and CashResult = 0
and ' + #AddToWhereSql + ' ORDER BY Tarih DESC'
exec sp_executesql #Sql
END
GO
As you pass in a Variable #MusNo of type BIGINT you must concatenate its value into your dynamic SQL. Try it like this:
SET #Sql = N'SELECT [KartNo],[HesapNo],[TCKN],[CepTel],[MusNo],[Alacakli],[Tutar],[Tarih],[AtmNo],[Borclu]
FROM [ATMDB].[isb].[NakitIslemSorgu] WITH (NOLOCK)
WHERE MusNo = ' + CAST(#MusNo AS VARCHAR(100)) + ' and cast([Tarih] as Date)>= #StartDate and cast([Tarih] as Date) <= #EndDate and CashResult = 0
and ' + #AddToWhereSql + ' ORDER BY Tarih DESC'
sp_executesql requires the variables to be declared and initialised. This is done using parameters as follows:
DECLARE #Params NVARCHAR(2000);
SET #Params = N'#MusNo bigint, #StartDate Date, #EndDate Date';
exec sp_executesql #Sql, #Params, #MusNo = #MusNo, #StartDate = #StartDate, #EndDate = #EndDate;
Even better is the following which will completely avoid any sql injection.
DECLARE #Sql nvarchar(max)
DECLARE #Params NVARCHAR(2000);
SET #Params = N'#IslemKodu varchar(30), #MusNo bigint, #StartDate Date, #EndDate Date';
SET #Sql = N'SELECT [KartNo],[HesapNo],[TCKN],[CepTel],[MusNo],[Alacakli],[Tutar],[Tarih],[AtmNo],[Borclu]
FROM [ATMDB].[isb].[NakitIslemSorgu] WITH (NOLOCK)
WHERE MusNo = #MusNo and cast([Tarih] as Date)>= #StartDate and cast([Tarih] as Date) <= #EndDate and CashResult = 0
and IslemKodu = ISNULL(#IslemKodu, IslemKodu) ORDER BY Tarih DESC'
exec sp_executesql #Sql, #Params, #IslemKodu = #IslemKodu, #MusNo = #MusNo, #StartDate = #StartDate, #EndDate = #EndDate;
I have a procedure to fetch data as per the criteria passed. I works fine with #month, #year, #quater.
ALTER PROCEDURE dbo.SPReportTimeSpan
(
#Week int = null,
#Month int = null,
#Year int = null,
#Quater int = null
)
AS
SET NOCOUNT ON
DECLARE #sql nvarchar(4000)
If (#Week) IS NOT NULL
BEGIN
DECLARE #startdate Date, #enddate Date
EXEC dbo.SPReturnStartEndDateOfSpecifiedWeek #Week, #startdate OUTPUT, #enddate OUTPUT
SELECT #startdate, #enddate
END
SELECT #sql='SELECT
CRMDR.Id as Id,.
CRMDR.Request_For_Id as Request_For_Id
From [CRM].[dbo].[CRM_Doctor_Request] AS CRMDR
WHERE CRMDR.Is_Deleted=0 '
If (#Month) IS NOT NULL
SELECT #sql=#sql + ' AND MONTH(CRMDR.Date_Created) like (#Month) '
If (#Year) IS NOT NULL
SELECT #sql=#sql + ' AND YEAR(CRMDR.Date_Created) like (#Year) '
If (#Week) IS NOT NULL
SELECT #sql=#sql + ' AND (CRMDR.Date_Created) BETWEEN (#startdate) AND (#enddate) '
If (#Quater) IS NOT NULL
IF (#Quater = 1)
SELECT #sql=#sql + ' AND MONTH(CRMDR.Date_Created) in (4,5,6) '
IF (#Quater = 2)
SELECT #sql=#sql + ' AND MONTH(CRMDR.Date_Created) in (7,8,9) '
IF (#Quater = 3)
SELECT #sql=#sql + ' AND MONTH(CRMDR.Date_Created) in (10,11,12) '
IF (#Quater = 4)
SELECT #sql=#sql + ' AND MONTH(CRMDR.Date_Created) in (1,2,3) '
SELECT #sql=#sql + ' ORDER BY CRMDR.Id DESC '
EXEC sp_executesql #sql, N'#Month int, #Year int, #Quater int',
#Month, #Year, #Quater
RETURN
But for #week it gives error.
Running [dbo].[SPReportTimeSpan] ( #Week = 3, #Month = <NULL>, #Year = <NULL>, #Quater = <NULL> ).
Must declare the scalar variable "#startdate".
Column1 Column2
------------------------------ ------------------------------
2013-02-10 00:00:00.0000000 2013-02-17 00:00:00.0000000
No rows affected.
(1 row(s) returned)
#RETURN_VALUE = 0
Finished running [dbo].[SPReportTimeSpan].
I am not getting why its not working with week criteria. Please help if anyone have idea.
#startdate and #enddate are only accessible within the scope of your IF statement:
If (#Week) IS NOT NULL
BEGIN
DECLARE #startdate Date, #enddate Date
EXEC dbo.SPReturnStartEndDateOfSpecifiedWeek #Week, #startdate OUTPUT, #enddate OUTPUT
SELECT #startdate, #enddate
END
Move the declaration outside of the IF statement if you wish to reference these variables elsewhere:
DECLARE #startdate Date, #enddate Date
If (#Week) IS NOT NULL
BEGIN
EXEC dbo.SPReturnStartEndDateOfSpecifiedWeek #Week, #startdate OUTPUT, #enddate OUTPUT
SELECT #startdate, #enddate
END
FYI: The area where you make reference to these variables outside of the IF statement is here:
If (#Week) IS NOT NULL
SELECT #sql=#sql + ' AND (CRMDR.Date_Created) BETWEEN (#startdate) AND (#enddate) '
I got solution of my problem. To solve the problem, I need to explicitly cast datetime values to a character type so the query string can be concatenated as expected.
If (#Week) IS NOT NULL
SELECT #sql=#sql + ' AND (CRMDR.Date_Created) BETWEEN ''' + convert(VARCHAR,#startdate) + ''' AND ''' +convert(VARCHAR,#enddate) + ''''
or this (More Optimized)
SELECT #sql=#sql + ' AND (CRMDR.Date_Created BETWEEN #startdate AND #enddate)'
which needs to add something to sp_executesql
EXEC sp_executesql #sql, N'#Month int, #Year int, #Quater int, #Week int, #startdate datetime, #enddate datetime ',
#Month, #Year, #Quater, #Week, #startdate, #enddate
Your variables are out of scope.
I've created the following stored procedure on sql:
CREATE PROCEDURE Sp_generate_report (#YEAR INT,
#MONTH INT)
AS
DECLARE #CATEGORY VARCHAR(20)
DECLARE #BEGIN_DATE DATE
DECLARE #END_DATE DATE
BEGIN
SELECT #BEGIN_DATE = Cast(( Cast(#YEAR AS VARCHAR) + '-'
+ Cast(#MONTH AS VARCHAR) + '-' + '1' ) AS DATE)
SELECT #END_DATE = Cast(( Cast(#YEAR AS VARCHAR) + '-' +
Cast(#MONTH AS VARCHAR) + '-' + '32' ) AS DATE)
SELECT TOP 1 #CATEGORY = Name
FROM dbo.Profitible_categories(#BEGIN_DATE, #END_DATE)
INSERT INTO dbo.MONTHLY_SUMMARY_REPORTS
VALUES (#CATEGORY)
END
The procedure was created successfully, but when I try to execute it with the following command:
EXECUTE SP_GENERATE_REPORT 2012, 7
I get this error message:
Msg 241, Level 16, State 1, Procedure SP_GENERATE_REPORT, Line 8
Conversion failed when converting date and/or time from character
string.
thanks in advance for the help.
You can calculate the last day of the month without all that nasty string concatenation or trying to guess which month it is and pick the last day.
CREATE PROCEDURE dbo.Sp_generate_report
#YEAR INT,
#MONTH INT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #BEGIN_DATE DATE = DATEADD(MONTH, #MONTH-1, CONVERT(DATE,
CONVERT(CHAR(4), YEAR) + '0101'));
DECLARE #END_DATE DATE = DATEADD(DAY, -1, DATEADD(MONTH, 1, #BEGIN_DATE));
INSERT INTO dbo.MONTHLY_SUMMARY_REPORTS
SELECT TOP (1) Name
FROM dbo.Profitible_categories(#BEGIN_DATE, #END_DATE);
END
GO
I need to accomplish the following in the stored procedure:
Pass parameterized column names.
Select the parameterized column names and provide total group by selected columns.
Code:
CREATE PROCEDURE sproc (
#column1 NVARCHAR(MAX),
#column2 NVARCHAR(MAX),
#startdate DATE,
#enddate DATE ) AS
BEGIN
DECLARE #sqlquery NVARCHAR(MAX) = 'SELECT #column1, #column2, SUM(amountcolumn)
FROM tablename
WHERE column3 = ''#value3'',
datecolumn BETWEEN ''#startdate'' AND ''#enddate''
GROUP BY #column1, #column2';
DECLARE #params NVARCHAR(MAX) = '#column1 VARCHAR(MAX),
#column2 VARCHAR(MAX),
#startdate DATE,
#enddate DATE';
EXEC sp_sqlexec #sqlquery, #params,
#column1 = #column1,
#column2 = #column2,
#startdate = #startdate,
#enddate = #enddate;
END
GO
Assuming #value3 is a string and is another parameter to the stored procedure, that datecolumn is in fact date, and ignoring the fact that I have no idea how you can have a schema where the grouping fields can be random like this (which you ignored in other recent questions here):
DECLARE #sql nvarchar(max) = N'SELECT '
+ QUOTENAME(#column1) + ', '
+ QUOTENAME(#column2) + ', SUM(amountcolumn)
FROM dbo.tablename
WHERE column3 = #value3
AND datecolumn BETWEEN #startdate AND #enddate
GROUP BY ' + QUOTENAME(#column1)
+ ', ' + QUOTENAME(#column2) + ';';
EXEC sys.sp_executesql #sql,
N'#value3 varchar(255), #startdate date, #enddate date',
#value3, #startdate, #enddate;
-- strongly recommend against sp_sqlexec
-- it is undocumented and unsupported
This also assumes you don't care about order (you probably do and will want to add ORDER BY as well as GROUP BY).
For more info on dynamic SQL and even further ways to protect yourself from user input:
sqlblog.org/dynamic-sql