Assigning variables to use in query - sql

I am moving from Oracle to SQL Server and I am noticing differences regarding assigning variables in a query. I wonder if someone could write me a simple example of how I can do this in SSMS please?
In the example below I am looking to assign the variable #date1 at the beginning of the select statement so that I can simply change the date at the top instead of having to change it several times in the query where #date1 is used several times.
SELECT *
FROM table
where date = #date1
Thanks

Based on your example the syntax would be as follows:
DECLARE #date1 DATETIME
SET #date1 = '2017-01-01 00:00:00.000'
Then reference #date1 in your query as you have above.
More broadly, the syntax is:
DECLARE #<name of variable> <type>
SET #<name of variable> = <value>

-- Simple declares
DECLARE #Variable1 VARCHAR(100)
DECLARE #Variable2 DATE
DECLARE #VariableTable TABLE (
numberColumnName INT,
textColumnName VARCHAR(MAX))
-- Chained declares
DECLARE
#Variable3 VARCHAR(100),
#Variable4 INT
-- Declare with initiation
DECLARE #Variable5 INT = 150
DECLARE #Variable6 DATE = '2018-05-05' -- Implicit conversion (varchar to date)
DECLARE #Variable7 FLOAT = 1945.15 * 1648.12 / #Variable5 -- Expressions can be used
DECLARE #Variable8 INT = (SELECT COUNT(1) FROM sys.objects)
-- Chained declares with initiation
DECLARE
#Variable9 VARCHAR(100) = 'Afla',
#Variable10 INT = 9164 * #Variable5
-- Change variable values (without declaring)
SET #Variable1 = 'Some value'
SET #Variable2 = CONVERT(DATE, GETDATE())
For your example:
DECLARE #DateFilter DATE = '2018-05-16' -- Use ISO standard date format (yyyy-MM-dd) when you hard-code them as literals
SELECT
*
FROM
YourTable AS T
WHERE
T.DateToFilter >= #DateFilter

DECLARE #date1 DATE = '2018-04-11'
This code may be fine, but be aware of dates formats :date (Transact-SQL)
and the need of using either Date, Datetime, or Datetime2.

Related

Multiple parameters error while creating function in SQL Server

I created a function, now rather passing static value I want to add parameter in the function but after calling function it start throwing an error:
Procedure or function dbo.hello has too many arguments specified.
Function :
Create Function dbo.hello
(#InputstartDate Date, #InputendDate Date)
Returns #attendanceTemp table(STUD_NAME VARCHAR(50),
ATTD_DATE DATE ,
attd_DATEs DATE,
Attendance VARCHAR(20))
As
Begin
Declare #startDate DATE
SET #startDate = #InputstartDate
Declare #endDate Date
SET #endDate = #InputendDate
Declare #dateDifference INT
SET #dateDifference = DATEDIFF(day, #startDate,#endDate) ;
Declare #count INT
SET #count = 0
DECLARE #myTable TABLE (STUD_ID int,
countdd int,
STUD_NAME varchar(50),
AttDate Date
)
While #count <= #dateDifference
Begin
Insert Into #myTable (STUD_ID, countdd, STUD_NAME, AttDate)
Values (1, 123, 'HAIDER', #startDate)
Set #count = #count +1
Set #startDate = DATEADD(day, 1, #startDate)
End
Insert Into #attendanceTemp
Select
tb.STUD_NAME, ATTD_DATE, tb.AttDate,
Case
When att.DETAIL Is Null
Then 'ABSENT'
When att.DETAIL = 'ATTENDACE'
Then 'PRESENT'
End As Attendance
from
#myTable tb
Left Join
ATTENDANCE att on tb.AttDate = att.ATTD_DATE
Where
att.STUD_ID = 1 or att.STUD_ID IS NULL
Return
END
Calling the function:
select *
from dbo.hello('2014-04-01', '2014-04-10');
Error:
Procedure or function dbo.hello has too many arguments specified
Possibly you first created the function with only one parameter.
Then made changes to the 'create function' script, and forgot to deploy?
I would;
1. DROP FUNCTION dbo.hello
2. CREATE FUNCTION dbo.hello, with you script
3. Try executing your function again.
The function seems to work fine (Though I cannot run a full test due to not having table 'ATTENDANCE')

ssrs Ignoring variable which is not declared

This is my part of the query which i am using in the Dataset in SSRS2008
DECLARE #SERVER VARCHAR(20) = null;
Declare #curDATEFIRST as Integer = ##DATEFIRST;
SET DATEFIRST 1;
IF #a=0 AND #b>1
BEGIN
DECLARE #NewStartDate DateTime = (SELECT TOP 1 StartDate FROM UserManagerDates WHERE EmployeeID=#b ORDER BY StartDate DESC);
IF #NewStartDate>#StartDate SET #StartDate = #NewStartDate;
END
Question:
I was copying my query into dataset from ssms and when i hit refresh in dataset i only get prompted for #a and #b in define query parameters. Actually i wasn't even declaring #startdate so i was expecting #startdate variable in define query parameters tab. What changes should i make to to get prompted for #startdate along with the other two variables?
You are setting the #startdate parameter, it is supposed to be populated by SSRS instead of your query, that could be the reason you are not being prompted.
I think you want to use #startdate to populate #NewStartDate, if so try this:
DECLARE #SERVER VARCHAR(20) = null;
Declare #curDATEFIRST as Integer = ##DATEFIRST;
SET DATEFIRST 1;
IF #a=0 AND #b>1
BEGIN
DECLARE #NewStartDate DateTime =
(SELECT TOP 1 StartDate FROM UserManagerDates
WHERE EmployeeID=#b ORDER BY StartDate DESC);
IF #NewStartDate>#StartDate SET #NewStartDate = #startdate;
END

SQL Function help- Daylight Savings calculation

All,
I wrote a function that essentially takes a timestamp and a shopper ID, and based on that shoppers zipcode work out the UTC offset and correct the time.
The problem I am having is: it's sloooow!
Can anyone see an easy way to speed it up ?
CREATE FUNCTION TimeModifier
(
-- Add the parameters for the function here
#InputDate datetime,#shopperid int
)
RETURNS datetime
AS
BEGIN
--Declare #shopperid int
--set #shopperid=25
-- Declare the return variable here
-- Declare the return variable here
DECLARE #Result datetime
Declare #zip nvarchar(10)
Declare #TimeofYear int
declare #Year int
declare #Winter int
declare #Summer int
select #year=datepart(yyyy,#InputDate)
--If 0 then its outside of the summer hours.
SELECT #timeofyear=count(*) FROM [d].[dbo].[DST-Dates] where #inputdate>=startdate and #inputdate<=enddate
select #zip=zip from d..shopper where shopperid=#shopperid
--Gets the UTC offset for winter and summer
select #winter=winter,#summer=summer FROM [MMD_Feed].[dbo].[ZipCodeZones] where zip=#zip
if(#TimeofYear=0)--IE is it Winter
set #Result=DATEADD(HH,#winter, #Inputdate)
else--Use summer offset
set #Result=DATEADD(HH,#summer, #Inputdate)
--select #Result
-- Return the result of the function
RETURN #Result
END
GO
Thanks
~J
Here is a total shot in the dark for converting this to an iTVF.
CREATE FUNCTION TimeModifier
(
-- Add the parameters for the function here
#InputDate datetime
, #shopperid int
)
RETURNS TABLE WITH SCHEMABINDING
AS RETURN
select DATEADD(Hour, case when t.TimeOfYear = 0 then z.winter else z.summer end, #InputDate) as MyResult
from d.dbo.shopper s
join [MMD_Feed].[dbo].[ZipCodeZones] z on s.zip = z.zip
cross apply
(
SELECT count(*) as TimeOfYear
FROM [d].[dbo].[DST-Dates]
where #inputdate >= startdate
and #inputdate <= enddate
) t
where s.shopperid = #shopperid

SQL Server FOR EACH Loop

I have the following SQL query:
DECLARE #MyVar datetime = '1/1/2010'
SELECT #MyVar
This naturally returns '1/1/2010'.
What I want to do is have a list of dates, say:
1/1/2010
2/1/2010
3/1/2010
4/1/2010
5/1/2010
Then i want to FOR EACH through the numbers and run the SQL Query.
Something like (pseudocode):
List = 1/1/2010,2/1/2010,3/1/2010,4/1/2010,5/1/2010
For each x in List
do
DECLARE #MyVar datetime = x
SELECT #MyVar
So this would return:-
1/1/2010
2/1/2010
3/1/2010
4/1/2010
5/1/2010
I want this to return the data as one resultset, not multiple resultsets, so I may need to use some kind of union at the end of the query, so each iteration of the loop unions onto the next.
edit
I have a large query that accepts a 'to date' parameter, I need to run it 24 times, each time with a specific to date which I need to be able to supply (these dates are going to be dynamic) I want to avoid repeating my query 24 times with union alls joining them as if I need to come back and add additional columns it would be very time consuming.
SQL is primarily a set-orientated language - it's generally a bad idea to use a loop in it.
In this case, a similar result could be achieved using a recursive CTE:
with cte as
(select 1 i union all
select i+1 i from cte where i < 5)
select dateadd(d, i-1, '2010-01-01') from cte
Here is an option with a table variable:
DECLARE #MyVar TABLE(Val DATETIME)
DECLARE #I INT, #StartDate DATETIME
SET #I = 1
SET #StartDate = '20100101'
WHILE #I <= 5
BEGIN
INSERT INTO #MyVar(Val)
VALUES(#StartDate)
SET #StartDate = DATEADD(DAY,1,#StartDate)
SET #I = #I + 1
END
SELECT *
FROM #MyVar
You can do the same with a temp table:
CREATE TABLE #MyVar(Val DATETIME)
DECLARE #I INT, #StartDate DATETIME
SET #I = 1
SET #StartDate = '20100101'
WHILE #I <= 5
BEGIN
INSERT INTO #MyVar(Val)
VALUES(#StartDate)
SET #StartDate = DATEADD(DAY,1,#StartDate)
SET #I = #I + 1
END
SELECT *
FROM #MyVar
You should tell us what is your main goal, as was said by #JohnFx, this could probably be done another (more efficient) way.
You could use a variable table, like this:
declare #num int
set #num = 1
declare #results table ( val int )
while (#num < 6)
begin
insert into #results ( val ) values ( #num )
set #num = #num + 1
end
select val from #results
This kind of depends on what you want to do with the results. If you're just after the numbers, a set-based option would be a numbers table - which comes in handy for all sorts of things.
For MSSQL 2005+, you can use a recursive CTE to generate a numbers table inline:
;WITH Numbers (N) AS (
SELECT 1 UNION ALL
SELECT 1 + N FROM Numbers WHERE N < 500
)
SELECT N FROM Numbers
OPTION (MAXRECURSION 500)
declare #counter as int
set #counter = 0
declare #date as varchar(50)
set #date = cast(1+#counter as varchar)+'/01/2013'
while(#counter < 12)
begin
select cast(1+#counter as varchar)+'/01/2013' as date
set #counter = #counter + 1
end
Off course an old question. But I have a simple solution where no need of Looping, CTE, Table variables etc.
DECLARE #MyVar datetime = '1/1/2010'
SELECT #MyVar
SELECT DATEADD (DD,NUMBER,#MyVar)
FROM master.dbo.spt_values
WHERE TYPE='P' AND NUMBER BETWEEN 0 AND 4
ORDER BY NUMBER
Note : spt_values is a Mircrosoft's undocumented table. It has numbers for every type. Its not suggestible to use as it can be removed in any new versions of sql server without prior information, since it is undocumented. But we can use it as quick workaround in some scenario's like above.
[CREATE PROCEDURE [rat].[GetYear]
AS
BEGIN
-- variable for storing start date
Declare #StartYear as int
-- Variable for the End date
Declare #EndYear as int
-- Setting the value in strat Date
select #StartYear = Value from rat.Configuration where Name = 'REPORT_START_YEAR';
-- Setting the End date
select #EndYear = Value from rat.Configuration where Name = 'REPORT_END_YEAR';
-- Creating Tem table
with [Years] as
(
--Selecting the Year
select #StartYear [Year]
--doing Union
union all
-- doing the loop in Years table
select Year+1 Year from [Years] where Year < #EndYear
)
--Selecting the Year table
selec]

Best way to calculate Max/Min of N columns in SQL Server

Ok, firstly I've seen this thread. But none of the solutions are very satisfactory. The nominated answer looks like NULLs would break it, and the highest-rated answer looks nasty to maintain.
So I was wondering about something like the following :
CREATE FUNCTION GetMaxDates
(
#dte1 datetime,
#dte2 datetime,
#dte3 datetime,
#dte4 datetime,
#dte5 datetime
)
RETURNS datetime
AS
BEGIN
RETURN (SELECT Max(TheDate)
FROM
(
SELECT #dte1 AS TheDate
UNION ALL
SELECT #dte2 AS TheDate
UNION ALL
SELECT #dte3 AS TheDate
UNION ALL
SELECT #dte4 AS TheDate
UNION ALL
SELECT #dte5 AS TheDate) AS Dates
)
END
GO
Main problems I see are that if there are only 3 fields to compare, you'd still have to specify NULL for the other 2, and if you wanted to extend it to six comparisons it would break existing use. If it was a parameterized stored procedure you could specify a default for each parameter, and adding new parameters wouldn't break existing references. The same method could also obviously be extended to other datatypes or stuff like Min or Avg. Is there some major drawback to this that I'm not spotting? Note that this function works whether some, all or none of the values passed to it are nulls or duplicates.
You can solve null issue with ISNULL function:
SELECT ISNULL(#dte1,0) AS TheDate
UNION ALL
SELECT ISNULL(#dte2,0) AS TheDate
UNION ALL
SELECT ISNULL(#dte3,0) AS TheDate
UNION ALL
SELECT ISNULL(#dte4,0) AS TheDate
UNION ALL
SELECT ISNULL(#dte5,0) AS TheDate) AS Dates
But it will only work with MAX functions.
Here is another suggestion: http://www.sommarskog.se/arrays-in-sql-2005.html
They suggest comma delimited values in a form of string.
The function takes as many parameters as you wish and looks like this:
CREATE FUNCTION GetMaxDate
(
#p_dates VARCHAR(MAX)
)
RETURNS DATETIME
AS
BEGIN
DECLARE #pos INT, #nextpos INT, #date_tmp DATETIME, #max_date DATETIME, #valuelen INT
SELECT #pos = 0, #nextpos = 1
SELECT #max_date = CONVERT(DATETIME,0)
WHILE #nextpos > 0
BEGIN
SELECT #nextpos = charindex(',', #p_dates, #pos + 1)
SELECT #valuelen = CASE WHEN #nextpos > 0
THEN #nextpos
ELSE len(#p_dates) + 1
END - #pos - 1
SELECT #date_tmp = CONVERT(DATETIME, substring(#p_dates, #pos + 1, #valuelen))
IF #date_tmp > #max_date
SET #max_date = #date_tmp
SELECT #pos = #nextpos
END
RETURN #max_date
END
And calling:
DECLARE #dt1 DATETIME
DECLARE #dt2 DATETIME
DECLARE #dt3 DATETIME
DECLARE #dt_string VARCHAR(MAX)
SET #dt1 = DATEADD(HOUR,3,GETDATE())
SET #dt2 = DATEADD(HOUR,-3,GETDATE())
SET #dt3 = DATEADD(HOUR,5,GETDATE())
SET #dt_string = CONVERT(VARCHAR(50),#dt1,21)+','+CONVERT(VARCHAR(50),#dt2,21)+','+CONVERT(VARCHAR(50),#dt3,21)
SELECT dbo.GetMaxDate(#dt_string)
Why not just:
SELECT Max(TheDate)
FROM
(
SELECT #dte1 AS TheDate WHERE #dte1 IS NOT NULL
UNION ALL
SELECT #dte2 AS TheDate WHERE #dte2 IS NOT NULL
UNION ALL
SELECT #dte3 AS TheDate WHERE #dte3 IS NOT NULL
UNION ALL
SELECT #dte4 AS TheDate WHERE #dte4 IS NOT NULL
UNION ALL
SELECT #dte5 AS TheDate WHERE #dte5 IS NOT NULL) AS Dates
That shoud take care of the null problem without introducing any new values
I would pass the Dates in XML (you could use varchar/etc, and convert to the xml datatype too):
DECLARE #output DateTime
DECLARE #test XML
SET #test = '<VALUES><VALUE>1</VALUE><VALUE>2</VALUE></VALUES>'
DECLARE #docHandle int
EXEC sp_xml_preparedocument #docHandle OUTPUT, #doc
SET #output = SELECT MAX(TheDate)
FROM (SELECT t.value('./VALUE[1]','DateTime') AS 'TheDate'
FROM OPENXML(#docHandle, '//VALUES', 1) t)
EXEC sp_xml_removedocument #docHandle
RETURN #output
That would address the issue of handling as many possibilities, and I wouldn't bother putting nulls in the xml.
I'd use a separate parameter to specify the datetype rather than customize the xml & supporting code every time, but you might need to use dynamic SQL for it to work.
A better option is to restructure the data to support column based min/max/avg as this is what SQL is best at.
In SQL Server 2005 you can use the UNPIVOT operator to perform the transformation.
Not always appropriate for every problem, but can make things easier if you can use it.
See:
http://msdn.microsoft.com/en-us/library/ms177410.aspx
http://blogs.msdn.com/craigfr/archive/2007/07/17/the-unpivot-operator.aspx
If you have to do it over one row only, it doesn't matter how you will do it (everything would be fast enough).
For selecting Min/Max/Avg value of several columns PER ROW, solution with UNPIVOT should be much faster than UDF
an other possibility is to create a custom table type, like this:
CREATE TYPE [Maps].[TblListInt] AS TABLE( [ID] [INT] NOT NULL )
then,
CREATE FUNCTION dbo.GetMax(#ids maps.TblListInt READONLY) RETURNS INT
BEGIN
RETURN (select max(id) from #ids)
END
Of course, you can swap "int" with your required type.