Adding Parameter causing issues-No data displayed - tsql

Hi all I have a stored procedure with two parameters #Startdate and #Enddate. When i execute the procedure i get data.
Now i added a parameter and it has list of values. So i added a split function and added in the WHERE clause. Now after making the changes when i execute my SP i do not get any data. I tried commenting out the 3rd Parameter from the WHERE clause and now i see the data again. Not sure what is happening. Any advice is greatly appreciated.
I have tried different split functions and Charindex(','+cast(tableid as varchar(8000))+',', #Ids) > 0 and nothing has worked.
Thanks

NOTE: The concatenation and splitting of parameter values is a poor design for performance reasons and, most importantly, very susceptible to SQL injection attacks. Please research some alternatives. If you must proceed down this path...
There are a great many split functions out there, but I used this one here to illustrate a possible solution.
CREATE FUNCTION [dbo].[fnSplitString]
(
#string NVARCHAR(MAX),
#delimiter CHAR(1)
)
RETURNS #output TABLE(splitdata NVARCHAR(MAX)
)
BEGIN
DECLARE #start INT, #end INT
SELECT #start = 1, #end = CHARINDEX(#delimiter, #string)
WHILE #start < LEN(#string) + 1 BEGIN
IF #end = 0
SET #end = LEN(#string) + 1
INSERT INTO #output (splitdata)
VALUES(SUBSTRING(#string, #start, #end - #start))
SET #start = #end + 1
SET #end = CHARINDEX(#delimiter, #string, #start)
END
RETURN
END
GO
It's unclear, from your question, if you need to filter your results based on an int, varchar or various other data types available, but here are two options (and probably the most common).
DECLARE #TableOfData TABLE
(
ID_INT INT,
ID_VAR VARCHAR(100),
START_DATE DATETIME,
END_DATE DATETIME
)
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
DECLARE #Ids VARCHAR(1000)
DECLARE #Delimiter VARCHAR(1)
SET #Delimiter = ','
SET #StartDate = GETDATE()
SET #EndDate = DATEADD(HH, 1, GETDATE())
SET #Ids = '1,2,4'
--Create some test data
INSERT INTO #TableOfData
SELECT 1, '1', GETDATE(), DATEADD(MI, 1, GETDATE()) --In our window of expected results (date + id)
UNION SELECT 2, '2', GETDATE(), DATEADD(D, 1, GETDATE()) --NOT in our window of expected results b/c of date
UNION SELECT 3, '3', GETDATE(), DATEADD(MI,2, GETDATE()) --NOT in our expected results (id)
UNION SELECT 4, '4', GETDATE(), DATEADD(MI,4, GETDATE()) --In our window of expected results (date + id)
--If querying by string, expect 2 results
SELECT TD.*
FROM #TableOfData TD
INNER JOIN dbo.fnSplitString(#Ids, #Delimiter) SS
ON TD.ID_VAR = SS.splitdata
WHERE START_DATE >= #StartDate
AND END_DATE <= #EndDate
--If querying by int, expect 2 results
SELECT TD.*
FROM #TableOfData TD
INNER JOIN dbo.fnSplitString(#Ids, #Delimiter) SS
ON TD.ID_INT = CONVERT(int, SS.splitdata)
WHERE START_DATE >= #StartDate
AND END_DATE <= #EndDate

You cannot use a parameter with a list directly in your query filter. Try storing that separated data into a table variable or temp table and call that in your query or use dynamic SQL to write your query if you don't want to use table variable or temp tables.

Related

SQL query to count number of records within a given timeframe, filling in the gaps

I am trying to count the number of records that fall between a given time period, generally 15 minutes, but I'd like to have this interval a variable. My table has a datetime column and I need to get a count of the number of records for every 15 minute interval between two dates and when there aren't any records for the 15-minute window, I need that time period with a zero. I've tried using CTE in different combinations and couldn't get it to work. I can generate the date series using a CTE, but haven't been able to get the actual data in the result. I have a working solution using a stored procedure with a WHILE loop. I was hoping to avoid this if possible in favor or a more elegant solution.
Here is my working loop using a temporary table:
declare #record_count int = 0
declare #end_date_per_query datetime
create table #output (
SessionIdTime datetime,
CallCount int)
while #date_from < #date_to
begin
set #end_date_per_query = DATEADD(minute, #interval, #date_from)
select #record_count = COUNT(*) from tbl WHERE SessionIdTime between #date_from and #end_date_per_query
insert into #output values (#date_from, #record_count)
set #date_from = #end_date_per_query
end
select * from #output order by sessionIdTime
drop table #output
Hopefully someone can help with a more elegant solution. Any help is appreciated.
A CTE works just fine:
-- Parameters.
declare #Start as DateTime = '20120901'
declare #End as DateTime = '20120902'
declare #Interval as Time = '01:00:00.00' -- One hour. Change to 15 minutes.
select #Start as [Start], #End as [End], #Interval as [Interval]
-- Sample data.
declare #Sessions as Table ( SessionId Int Identity, SessionStart DateTime )
insert into #Sessions ( SessionStart ) values
( '20120831 12:15:07' ), ( '20120831 21:51:18' ),
( '20120901 12:15:07' ), ( '20120901 21:51:18' ),
( '20120902 12:15:07' ), ( '20120902 21:51:18' )
select * from #Sessions
-- Summary.
; with SampleWindows as (
select #Start as WindowStart, #Start + #Interval as WindowEnd
union all
select SW.WindowStart + #Interval, SW.WindowEnd + #Interval
from SampleWindows as SW
where SW.WindowEnd < #End
)
select SW.WindowStart, Count( S.SessionStart ) as [Sessions]
from SampleWindows as SW left outer join
#Sessions as S on SW.WindowStart <= S.SessionStart and S.SessionStart < SW.WindowEnd
group by SW.WindowStart
Is this what you're looking for?
-- setup
DECLARE #interval INT, #start DATETIME
SELECT #interval = 15, #start = '1/1/2000 0:00:00'
DECLARE #t TABLE (id INT NOT NULL IDENTITY(1,1) PRIMARY KEY, d DATETIME)
INSERT INTO #t (d) VALUES
(DATEADD(mi, #interval * 0.00, #start)) -- in
,(DATEADD(mi, #interval * 0.75, #start)) -- in
,(DATEADD(mi, #interval * 1.50, #start)) -- out
-- query
DECLARE #result INT
SELECT #result = COUNT(*) FROM #t
WHERE d BETWEEN #start AND DATEADD(mi, #interval, #start)
-- result
PRINT #result

How do you initialize a variable in a stored procedure with a function

How do you initialize a variable in a stored procedure with a function?
This doesn't work:
/****** Object: StoredProcedure [dbo].[usp_ShowBackpopGaps] Script Date: 05/25/2011 19:57:23 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: <Author,,Name>
-- Create date: <Create Date,,>
-- Description: <Description,,>
-- =============================================
ALTER PROCEDURE [dbo].[usp_ShowBackpopGaps]
-- Add the parameters for the stored procedure here
#StartDate datetime = DateAdd(yy, -1,getdate()),
#EndDate datetime = getdate
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
;with dateranges as(
select DateAdd(dd,-1,EtlStartDate) as DateFrom,
DateAdd(dd,1,EtlEndDate) as DateTo
from EtlJobRunStatus
where JobRunStepID like 'ETL[0-9]%' and EndTime is not null
union all
select #StartDate ,#StartDate
union all
select DateAdd(dd,-1,#EndDate),DateAdd(dd,-1,#EndDate)
)
select DateAdd(dd,-1,DateTo) as MissingFrom,
DateAdd(dd,1,NextDateFrom) as MissingTo,
DateDiff(d,DateTo, NextDateFrom) as MissingDays
from (
select distinct DateFrom, DateTo as DateTo,
(select MIN (dateFrom)
from dateranges
where DateTo > D.DateTo
) as NextDateFrom
from dateranges D
) X
where DateTo < NextDateFrom
END
GO
You can't have a function call as a parameter default.
I think you need
CREATE PROCEDURE [dbo].[usp_ShowBackpopGaps]
#StartDate DATETIME = NULL,
#EndDate DATETIME = NULL
AS
BEGIN
SET #StartDate = ISNULL(#StartDate,DATEADD(yy, -1,GETDATE()))
SET #EndDate = ISNULL(#EndDate, GETDATE())
...

parser datetime via tsql

Update List
set Date = "2009-07-21T19:00:40"
sql server doesn't recognize this format. is there a conversion function
You can use CAST and CONVERT. But maybe you must replace the 'T' with a space, before you can convert it. (There are also string manipulation functions available.)
Worked just fine for me (SQL Express 2005) except for the double quotes (which SQL Server assumed was a column delimiter); is that what's throwing the error? Thanks for the sample code, but can you produce the actual error?
In other words,
DECLARE #List TABLE ( [date] DATETIME
)
INSERT INTO #List
SELECT GETUTCDATE()
UPDATE #List SET Date =
"2009-07-21T19:00:40"
produces
Msg 207, Level 16, State 1, Line 7
Invalid column name
'2009-07-21T19:00:40'.
Whereas
DECLARE #List TABLE ( [date] DATETIME
)
INSERT INTO #List
SELECT GETUTCDATE()
UPDATE #List SET Date =
'2009-07-21T19:00:40'
runs successfully.
Try this:
UPDATE List SET Date = '2009/07/21 19:00:40'
Even worked for me too(2005 & 2008)..
DECLARE #tbl TABLE ( [date] DATETIME )
INSERT INTO #tbl SELECT getdate()
select * from #tbl
UPDATE #tbl SET Date = '2009-07-21T19:00:40'
select * from #tbl
However, just give a shot with DATEFORMAT COMMAND
Something like this
**SET DATEFORMAT dmy**
DECLARE #tbl TABLE ( [date] DATETIME )
INSERT INTO #tbl SELECT getdate()
select * from #tbl
UPDATE #tbl SET Date = '2009-07-21T19:00:40'
select * from #tbl

Implementing and applying a string split in T-SQL

I have this statement in T-SQL.
SELECT Bay From TABLE where uid in (
select B_Numbers from Info_Step WHERE uid = 'number'
)
I am selecting "multiple" BAYs from TABLE where their uid is equal to a string of numbers like this:
B_Numbers = 1:45:34:98
Therefore, I should be selecting 4 different BAYs from TABLE. I basically need to split the string 1:45:34:98 up into 4 different numbers.
I'm thinking that Split() would work, but it doesn't and I get a syntax error.
Any thoughts from the T-SQL gods would be awesome!
Here is an implementation of a split function that returns the list of numbers as a table:
http://rbgupta.blogspot.com/2007/03/split-function-tsql.html
Looks like this would set you on your way...
Here is a method that uses an auxiliary numbers table to parse the input string. The logic can easily be added to a function that returns a table. That table can then be joined to lookup the correct rows.
Step 1: Create the Numbers table
SET NOCOUNT ON
GO
IF EXISTS
(
SELECT 1
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'Numbers'
AND TABLE_SCHEMA = 'dbo'
AND TABLE_TYPE = 'BASE TABLE'
)
BEGIN
DROP TABLE dbo.Numbers
END
GO
CREATE TABLE dbo.Numbers
(
Number smallint IDENTITY(1, 1) PRIMARY KEY
)
GO
WHILE 1 = 1
BEGIN
INSERT INTO dbo.Numbers DEFAULT VALUES
IF SCOPE_IDENTITY() = 32767
BEGIN
BREAK
END
END
GO
Step 2: Parse the Input String
CREATE FUNCTION dbo.ParseString(#input_string varchar(8000), #delim varchar(8000) = " ")
RETURNS TABLE
AS RETURN
(
SELECT Number
FROM dbo.Numbers
WHERE CHARINDEX
(
#delim + CONVERT(VARCHAR(12),Number) + #delim,
#delim + #input_string + #delim
) > 0
)
GO
**EXAMPLE**
SELECT * FROM dbo.ParseString('1:45:34:98',':')
Step 3: Use the results however you want/need
Number
------
1
34
45
98
End-To-End Example
Create function that returns the appropriate BNumber (of course change it to use the commented out SQL)
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION dbo.GetBNumber (#uid int)
RETURNS VARCHAR(8000)
AS
BEGIN
RETURN '1:45:34:98'
--select B_Numbers from Info_Step WHERE uid = #uid
END
GO
Use the use functions to return the desired results
-- Using Test Data
SELECT N.Number FROM Numbers N
JOIN dbo.ParseString(dbo.GetBNumber(12345),':') Q ON Q.Number = N.Number
-- Using Your Data (Untested but should work.)
SELECT N.Bay
FROM TABLE N
JOIN dbo.ParseString(dbo.GetBNumber(ENTER YOU NUMBER HERE),':') Q ON Q.Number = N.uid
Results
Number
------
1
34
45
98
You should keep your arrays as rows but if I understand your question I think this will work.
SELECT
Bay
From
TABLE
join Info_Step
on B_Numbers like '%'+ uid +'%'
where
Info_Step.uid = 'number'
This query will do a full table scan because of the like operator.
What you can do is loop through the B_Numbers entries and do your own split on : Insert those entries into a temp table and then perform your query.
DECLARE #i int
DECLARE #start int
DECLARE #B_Numbers nvarchar(20)
DECLARE #temp table (
number nvarchar(10)
)
-- SELECT B_Numbers FROM Info_Step WHERE uid = 'number'
SELECT #B_Numbers = '1:45:34:98'
SET #i = 0
SET #start = 0
-- Parse out characters delimited by ":";
-- Would make a nice user defined function.
WHILE #i < len(#B_Numbers)
BEGIN
IF substring(#B_Numbers, #i, 1) = ':'
BEGIN
INSERT INTO #temp
VALUES (substring(#B_Numbers, #start, #i - #start))
SET #start = #i + 1
END
SET #i = #i + 1
END
-- Insert last item
INSERT INTO #temp
VALUES (substring(#B_Numbers, #start, #i - #start + 1))
-- Do query with parsed values
SELECT Bay FROM TABLE WHERE uid in (SELECT * FROM #temp)
You can even try this
declare #str varchar(50)
set #str = '1:45:34:98'
;with numcte as(
select 1 as rn union all select rn+1 from numcte where rn<LEN(#str)),
getchars as(select
ROW_NUMBER() over(order by rn) slno,
rn,chars from numcte
cross apply(select SUBSTRING(#str,rn,1) chars)X where chars = ':')
select top 1
Bay1 = SUBSTRING(#str,0,(select rn from getchars where slno = 1))
,Bay2 = SUBSTRING(#str,
(select rn from getchars where slno = 1) + 1,
(((select rn from getchars where slno = 2)-
(select rn from getchars where slno = 1)
)-1))
,Bay3 = SUBSTRING(#str,
(select rn from getchars where slno = 2) + 1,
(((select rn from getchars where slno = 3)-
(select rn from getchars where slno = 2)
)-1))
,Bay4 = SUBSTRING(#str,
(select rn from getchars where slno = 3)+1,
LEN(#str))
from getchars
Output:
Bay1 Bay2 Bay3 Bay4
1 45 34 98

sql missing rows when grouped by DAY, MONTH, YEAR

If I select from a table group by the month, day, year,
it only returns rows with records and leaves out combinations without any records, making it appear at a glance that every day or month has activity, you have to look at the date column actively for gaps. How can I get a row for every day/month/year, even when no data is present, in T-SQL?
Create a calendar table and outer join on that table
My developer got back to me with this code, underscores converted to dashes because StackOverflow was mangling underscores -- no numbers table required. Our example is complicated a bit by a join to another table, but maybe the code example will help someone someday.
declare #career-fair-id int
select #career-fair-id = 125
create table #data ([date] datetime null, [cumulative] int null)
declare #event-date datetime, #current-process-date datetime, #day-count int
select #event-date = (select careerfairdate from tbl-career-fair where careerfairid = #career-fair-id)
select #current-process-date = dateadd(day, -90, #event-date)
while #event-date <> #current-process-date
begin
select #current-process-date = dateadd(day, 1, #current-process-date)
select #day-count = (select count(*) from tbl-career-fair-junction where attendanceregister <= #current-process-date and careerfairid = #career-fair-id)
if #current-process-date <= getdate()
insert into #data ([date], [cumulative]) values(#current-process-date, #day-count)
end
select * from #data
drop table #data
Look into using a numbers table. While it can be hackish, it's the best method I've come by to quickly query missing data, or show all dates, or anything where you want to examine values within a range, regardless of whether all values in that range are used.
Building on what SQLMenace said, you can use a CROSS JOIN to quickly populate the table or efficiently create it in memory.
http://www.sitepoint.com/forums/showthread.php?t=562806
The task calls for a complete set of dates to be left-joined onto your data, such as
DECLARE #StartInt int
DECLARE #Increment int
DECLARE #Iterations int
SET #StartInt = 0
SET #Increment = 1
SET #Iterations = 365
SELECT
tCompleteDateSet.[Date]
,AggregatedMeasure = SUM(ISNULL(t.Data, 0))
FROM
(
SELECT
[Date] = dateadd(dd,GeneratedInt, #StartDate)
FROM
[dbo].[tvfUtilGenerateIntegerList] (
#StartInt,
,#Increment,
,#Iterations
)
) tCompleteDateSet
LEFT JOIN tblData t
ON (t.[Date] = tCompleteDateSet.[Date])
GROUP BY
tCompleteDateSet.[Date]
where the table-valued function tvfUtilGenerateIntegerList is defined as
-- Example Inputs
-- DECLARE #StartInt int
-- DECLARE #Increment int
-- DECLARE #Iterations int
-- SET #StartInt = 56200
-- SET #Increment = 1
-- SET #Iterations = 400
-- DECLARE #tblResults TABLE
-- (
-- IterationId int identity(1,1),
-- GeneratedInt int
-- )
-- =============================================
-- Author: 6eorge Jetson
-- Create date: 11/22/3333
-- Description: Generates and returns the desired list of integers as a table
-- =============================================
CREATE FUNCTION [dbo].[tvfUtilGenerateIntegerList]
(
#StartInt int,
#Increment int,
#Iterations int
)
RETURNS
#tblResults TABLE
(
IterationId int identity(1,1),
GeneratedInt int
)
AS
BEGIN
DECLARE #counter int
SET #counter= 0
WHILE (#counter < #Iterations)
BEGIN
INSERT #tblResults(GeneratedInt) VALUES(#StartInt + #counter*#Increment)
SET #counter = #counter + 1
END
RETURN
END
--Debug
--SELECT * FROM #tblResults