Stored Procedure Where Clause Parameters - asp.net

I've got an ASP.net search page where the user can enter one or more search criteria. The page calls a stored procedure to query a MS SQL Server 2008 db.
Part of the search criteria is single date or date range. If the user supplies Date1, we search on a single date. If the user supplies Date1 and Date2, we search on a date range.
My issue is coding this logic in the stored proc.
#Date1 datetime
#Date2 datetime
..other search params...
So there are three conditions:
Both #Date1 and #Date2 are null (the user is not searching on dates)
#Date1 is not null and #Date2 is null (the user is searching on a single date)
#Date1 is not null and #Date2 is not null (user is searching a date range)
I can't figure out how to structure the WHERE clause to handle each of the three possible conditions.
I'm familiar with ISNULL() and COALESCE()
Any tips or suggestions are greatly appreciated.

CREATE PROCEDURE BLABLABLA(
#DATE1 DATETIME = NULL,
#DATE2 DATETIME = NULL
)
AS
BEGIN
SELECT COL1, COL2
FROM THE_TABLE
WHERE
THE_TABLE.DATETIMEFIELD BETWEEN
ISNULL(#DATE1, THE_TABLE.DATETIMEFIELD)
AND COALESCE(#DATE2, #DATE1, THE_TABLE.DATETIMEFIELD)
END
Another choice, losing some expressiveness but likely using indexes, could be:
CREATE PROCEDURE BLABLABLA(
#DATE1 DATETIME = NULL,
#DATE2 DATETIME = NULL
)
AS
BEGIN
SELECT COL1, COL2
FROM THE_TABLE
WHERE
(THE_TABLE.DATETIMEFIELD >= #DATE1 OR #DATE1 IS NULL)
AND (THE_TABLE.DATETIMEFIELD <= #DATE2
OR THE_TABLE.DATETIMEFIELD = #DATE1
OR (#DATE1 IS NULL AND #DATE2 IS NULL))
END

You could try to create your SQL query as a string in the SP and then execute it, like this:
...
declare #sql varchar(500)
set #sql = 'select from myTable where 1=1'
if(#Date1 <> null)
set #sql = #sql + ' and date1 >= '+ #date1
if(#Date2 <> null)
set #sql = #sql + ' and date2 <= '+ #date2
print(#sql) -- for debug
exec(#sql)

Related

Return 0 if the date for a certain day does not have values

I'm doing a query to return the number of count records for a certain date.
The problem is when I use the GroupBy by a certain day, If the date have no records then the date for that day will not be shown in the output.
How can I achieve that?
I'm doing something like:
SELECT COUNT(History.Id)
FROM History
INNER JOIN User ON User.Id = History.UserId
WHERE (#StartDate = #NullDate OR History.CreatedOnDate >= #StartDate)
AND (#EndDate = #NullDate OR History.CreatedOnDate <= #EndDate)
GROUP BY History.CreatedOnDat
Example
01-08, 3 records
02-08, 2 records
04-08, 5 records
I need to have 03-08 with 0 records.
Create a temp table with one day per row:
Declare #StartDate datetime = '2016-08-01'
Declare #EndDate datetime = '2016-08-31'
declare #temp table
(
oneday datetime
);
WHILE #StartDate <= #EndDate
begin
INSERT INTO #temp VALUES (#StartDate);
SET #StartDate = Dateadd(Day,1, #StartDate);
end
select * from #temp
Then, simply join your query with this temp table.

Condensing several SQL queries into a single query so a parameter can be used for different data types

I'm looking to Condense several SQL Server queries into a single query so a parameter can be used for different data types. These types are dates, or numbers or strings. The parameter is called: #SearchValue.
In a strongly typed DataSet we have the 3 queries listed below.
This is for ASP.Net with a VB.Net code-behind file but I think this question is may also be good for non ASP.Net as well.
I call this one if the user enters a date into a search TextBox:
Query:
SELECT ID, PaymentAmount, PaymentDate, WhatWasPaymentFor
FROM Payments
WHERE (ParentID = #ParentID) AND
(PaymentDate = #SearchValue)
Call from VB.Net code-behind for the date search query:
tblObject = theTableAdapter.GetDataByPaymentDate(dcmParentsId, TextBoxSearch.Text)
If tblObject.Count() > 0 Then
GridViewSummary.DataSource = tblObject
GridViewSummary.DataBind()
End If
The other ones are for numbers only and the last one is for everything else.
This one is for numbers only:
SELECT PaymentDate, PaymentAmount, WhatWasPaymentFor, ID
FROM Payments
WHERE (ParentID = #ParentID) AND
(PaymentAmount = #SearchValue)
This one is called when the other 2 queries don't find any data:
SELECT PaymentDate, PaymentAmount, WhatWasPaymentFor, ID
FROM Payments
WHERE (ParentID = #ParentID) AND
((WhatWasPaymentFor LIKE '%' + #SearchValue + '%') OR
(#SearchValue = 'ALL'))
All of this coding works as is and I did it this way because there would be an error if I tried to call .GetDataByPaymentDate with a non date value.
Is there a way to use a single query to handle the searching by dates, numbers, and strings?
* UPDATES *
Thanks for all the sample queries. I am trying all of the sample queries in the SQL Server Management Studio to see what results come up.
I this one based on Gordon's query but it does not return any data:
DECLARE #SearchValue VARCHAR = '01/01/2012'
DECLARE #SearchType VARCHAR = 'Dates'
DECLARE #ParentID INT = 3
SELECT ID, PaymentAmount, PaymentDate, WhatWasPaymentFor
FROM Payments cross join
(select #SearchValue as sv) const
WHERE ParentID = #ParentID AND
(case when #SearchType = 'Dates' and ISDATE(const.sv) = 1
then (case when PaymentDate = CAST(const.sv AS datetime) then 'true' else 'false' end)
when #SearchType = 'Numbers' and ISNUMERIC(const.sv) = 1
then (case when PaymentAmount = cast(const.sv as Int) then 'true' else 'false' end)
when #SearchType = 'Everything Else'
then (case when WhatWasPaymentFor LIKE '%' + const.sv + '%' OR const.sv='ALL' then 'true' else 'false' end)
end) = 'true'
This is based on the one from gh9 and pulls up data. Thanks gh9:
DECLARE #SearchValue VARCHAR = 'Books'
DECLARE #ParentID INT = 3
DECLARE #PaymentDate DATETIME = NULL
DECLARE #PaymentAmount MONEY = NULL
SELECT ID, PaymentAmount, PaymentDate, WhatWasPaymentFor
FROM Payments
WHERE ParentID = #ParentID
AND (#paymentDate is null OR PaymentDate = #Paymentdate)
AND (#paymentAmount is null OR paymentAmount = #paymentAmount)
AND ((#SearchValue is null OR
(WhatWasPaymentFor LIKE '%' + #SearchValue + '%' OR #SearchValue='ALL'))
)
This lets you take advantage of not having to cast searchvalue to whatever you need, also is a bit more readable. Modify the syntax as needed, but the key idea is to use sql server ability to have null parameters and short circuit logic to evaluate strongly typed parameters instead of casting to the data type you need.
#ParentID INT
#PaymentAmount INT = NULL
#PaymentDate Datetime = null
#GenericSearchTerm varchar(100) = null
AS
BEGIN
SELECT
ID,
PaymentAmount,
PaymentDate,
WhatWasPaymentFor
FROM Payments
WHERE #ParentID = #ParentID
AND ( (#paymentDate is null OR PaymentDate = #Paymentdate))
AND (#paymentAmount is null OR paymentAmount = #paymentAmount))
AND ( #GenericSearchTerm is null OR ((WhatWasPaymentFor LIKE '%' + #GenericSearchTerm + '%' OR #SearchValue='ALL'))
EDIT:updated answer per #andriyM comments
How about adding a third parameter of #SearchType with possible values of ('datetime','int', or 'nvarchar') and using it in your WHERE clause to cast the #SearchValue to appropriate type for comparison. Something like:
SELECT
ID,
PaymentAmount,
PaymentDate,
WhatWasPaymentFor
FROM Payments
WHERE #ParentID = #ParentID
AND (
(#SearchType = 'datetime' AND PaymentDate = CAST(#SearchValue AS datetime))
OR
(#SearchType = 'int' AND PaymentAount = CAST(#SearchValue AS int))
OR
(#SearchType = 'nvarchar' AND
(WhatWasPaymentFor LIKE '%' + #SearchValue + '%' OR #SearchValue='ALL')
);
This is inspired by Bstateham's answer, but it does two things differently. First, it stores the values in a table, turning them from constants into variables. Second, it uses the case because that guarantees shortcircuiting.
This results in something like:
SELECT ID, PaymentAmount, PaymentDate, WhatWasPaymentFor
FROM Payments cross join
(select #SearchValue as sv) const
WHERE #ParentID = #ParentID AND
(case when #SearchType = 'datetime' and ISDATE(const.sv) = 1
then (case when PaymentDate = CAST(const.sv AS datetime) then 'true' else 'false' end)
when #SearchType = 'int' and ISNUMERIC(const.sv) = 1
then (case when PaumentAmount = cast(const.sv as Int) then 'true' else 'false' end)
when #SearchType = 'nvarchar'
then (case when WhatWasPaymentFor LIKE '%' + const.sv + '%' OR const.sv='ALL' then 'true' else 'false' end)
end) = 'true'
I also added in validation checks for the conversion to integer and date (not perfect, but will help). Also, you should probably name the #SearchType after the destination field rather than the type. However, I've kept the version with the type name.

Using Date.Now in SQL Function

I have a Function like this;
ALTER FUNCTION [dbo].[fngcodeme]
(
#HESAP INT, #DOV INT, #TEKLIF VARCHAR(10), #BAS datetime, #BIT datetime
)
RETURNS FLOAT
AS
BEGIN
DECLARE #Result FLOAT
IF CONVERT(DATETIME, #BIT,103) <= '20110228'
SET #Result = (SELECT SUM(TUTAR)
FROM YAZ..MARDATA.M_HAREKET
WHERE TEMEL_HESAP = #HESAP
AND DOVIZ_KOD = #DOV
AND REF_KOD = 'GC'
AND BACAK_GRUP = 'PERT'
AND ISL_KOD = 1
AND ACIKLAMA LIKE '%' + #TEKLIF + '%'
AND ISL_TAR >= CONVERT(DATETIME, #BAS,103)
AND ISL_TAR <= CONVERT(DATETIME, #BIT,103)
)
ELSE
SET #Result = (SELECT SUM(TUTAR)
FROM YAZ..MARDATA.M_GHAREKET
WHERE TEMEL_HESAP = #HESAP
AND DOVIZ_KOD = #DOV
AND REF_KOD = 'GC'
AND BACAK_GRUP = 'PERT'
AND ISL_KOD = 1
AND ACIKLAMA LIKE '%' + #TEKLIF + '%'
AND ISL_TAR >= CONVERT(DATETIME, #BAS,103)
AND ISL_TAR <= CONVERT(DATETIME, #BIT,103)
)
RETURN #Result
END
M_GHAREKET table is my monthly data table. Every end of the month this table load to M_HAREKET. (And deleted all data in M_GHAREKET)
M_HAREKET is a Big Bucket. M_GHAREKET is small. (I mean M_HAREKET has now over 500.000 data, M_GHAREKET over 4.000)
What i want is for this Function, IF #BAS date's month value is equal DateTime.Now.Month value i want use M_GHAREKET table, else use M_HAREKET table.
How should i change this function like that?
Is it possible like this IF clause with DateTime.Now.Month in SQL Function?
Or i should use in my ASP.NET page? How can i do that?
You can use month and year.
declare #BAS datetime
set #BAS = '2011-03-15'
if month(getdate()) = month(#BAS) and
year(getdate()) = year(#BAS)
You can use DATEDIFF to find the difference between two date in months. This returns the number of time period boundaries crossed so when checking for months it would only return 0 if the two dates are in the same calendar year and month
DECLARE #date1 DATETIME
SET #date1 = '2011-03-01'
SELECT DATEDIFF(MM, #date1, '2011-02-28') -- -1
SELECT DATEDIFF(MM, #date1, '2011-03-20') -- 0
SELECT DATEDIFF(MM, #date1, '2010-03-01') -- -12
So to check that a date is in the same month and year as today (GETDATE()) you could use
DECLARE #date1 DATETIME
SET #date1 = '2011-03-01'
IF DATEDIFF(MM, #date1, GETDATE()) = 0
BEGIN
-- Do your work
END

Does SQLite have Cursors?

i wonder if i could run the following procedure in SQLite:
set nocount on
select T.ID, max(T.SerialNo) as SerialNo
into #Tmp_Ticket_ID
from Ticket as T, Ticket as inserted
where t.ID = inserted.ID
group by T.id having count(*) > 1
declare zeiger cursor for
select SerialNo
from #Tmp_Ticket_ID
declare #SerialNo int
OPEN Zeiger
FETCH NEXT FROM zeiger INTO #SerialNo
WHILE (##fetch_status <> -1)
BEGIN
IF (##fetch_status <> -2)
BEGIN
update T
set ID = (select max(id) + 1 from Ticket)
from ticket AS T, #Tmp_Ticket_ID as I
where t.serialNo = i.serialno
and I.Serialno = #SerialNo
END
FETCH NEXT FROM zeiger INTO #SerialNo
END
CLOSE Zeiger
DEALLOCATE Zeiger
DROP TABLE #Tmp_Ticket_ID
This is a little procedure from a ms-sql2000 which cleans doubles of Ticket_id's in a given table Ticket of the following structur:
create table Ticket (serialNo int identity(1,1) not null
, ID as int not null
, Ticket_issue as varchar(50)
, some_more_field varchar(500))
Due to a simple merger from different databases, the ticket_id's becomes not unique. To fix by renumbering this i developed this procedure but now we have a similar issue on a SQLite-db.
delete from Ticket
where exists
(select rowid from Ticket t2
where t2.ID = Ticket.ID and t2.rowid < Ticket.rowid)
rowid is the always-present SQLite btree index column.
Thanks to Martin Engelschalk on the SQLite mailing list on 2009-08-17.

Stored proc in .net dataset class vs studio management

Morning all. Got myself a simple query which returns ten rows in SQL Server Management Studio. I call the stored proc by right clicking it and feeding in the parameters.
The results are returned immediately.
In .NET we have set up a dataset class, added a table adapter whose select is this same procedure. I pass in the very same parameters and the execution times out after the standard 30 seconds. It continues to run immediately when called in sql server management studio.
Any ideas why the execution time is seemingly infinite in the .net dataset class.
The query is very simple.
ALTER PROCEDURE [dbo].[usp_GetPostcodeAnalysis]
-- Add the parameters for the stored procedure here
#startDate DATETIME,
#endDate DATETIME,
#customerID INTEGER = NULL,
#siteID INTEGER = -1
AS
DECLARE #itemTypeID INT
SELECT #itemTypeID=ItemTypeID FROM dbo.ItemTypes WHERE ItemTypeName = 'Advert'
DECLARE #SQL NVARCHAR(4000)
BEGIN
SET #SQL = 'SELECT at.ActionTypeName,
COUNT(*) AS "Count"
FROM CustomerSites cs JOIN Items i
ON cs.SiteID = i.SiteID
JOIN Actions a
ON a.ItemID = i.ItemID
JOIN ActionTypes at
ON a.ActionTypeID = at.ActionTypeID
WHERE a.DateAndTime BETWEEN #1 AND #2
AND i.ItemTypeID = #iti
AND at.ActionTypeName IN (''List view'', ''Full view'', ''Email enquiry'', ''Print view'', ''Directions'')'
IF #customerID IS NOT NULL
SET #SQL = #SQL + ' AND i.CustomerID = #c'
IF #siteID > -1
SET #SQL = #SQL + ' AND i.SiteID = #s'
SET #SQL = #SQL + ' GROUP BY
at.ActionTypeName
ORDER BY
at.ActionTypeName'
EXECUTE sp_executesql #SQL,
N'#1 DATETIME, #2 DATETIME, #iti INT, #c INT, #s INT',
#startDate, #endDate, #itemTypeID, #customerID, #siteID

Resources