I'm having problems getting back the data I'd expect from a stored procedure. The procedure is used to both insert and update record, and this determines which parameters are set when called. My example here is assuming the DATE type parameter has the default value of NULL, i.e. they have not been passed into the sp. I have broken the code down into a small section to fix, rather than include the entire procedure code, as follows:
-- these would be sp parameters
declare #CustomerId int = 15
declare #Indicator varchar(5) = 'Yes'
declare #ProjectTypeId tinyint = 1
declare #FutureEffectiveDate as date = null
SELECT
CASE #FutureEffectiveDate
WHEN NULL THEN
CASE #Indicator
WHEN 'Yes' THEN
-- can only be 1, 2 or 3 to return relevant date
CASE #ProjectTypeId
WHEN 1 THEN DI.[NextFormalEffectiveDate]
WHEN 2 THEN DI.[NextInterimEffectiveDate]
WHEN 3 THEN DI.[NextAccountingEffectiveDate]
END
-- data should be NULL if #Indicator not 'Yes'
ELSE NULL
END
ELSE #FutureEffectiveDate
END AS [FutureEffectiveDate]
FROM
[_Staging].[DataImport_2] AS DI
JOIN
[CustomerView] AS CV ON CV.[CustomerNumber] = DI.[BillingInvoiced]
JOIN
[ProjectType] AS PT ON PT.[ProjectType] = DI.[ProjectType]
WHERE
CV.[CustomerID] = #CustomerId AND
PT.[ProjectTypeID] = #ProjectTypeId
So the idea is that, for records where a field contains the text 'Yes', and based on the project type for that record, it selects one of three dates. If the field is not 'Yes' then it should return NULL, ignoring the project type. If the date parameter is NOT null, then it should simply return the parameter passed in. The result is returned as the column 'FutureEffectiveDate'. With the example data I have, I would expect a date to be returned as the relevant field is 'Yes', and the column NextFormalEffectiveDate has a value (as project type is 1).
Oddly enough, if you exclude the outer CASE statement, it works. So the issue is around determining what to do based on the DATE parameter, but i cannot see why the outer CASE statement is breaking the result.
The way you checked #FutureEffectiveDate for NULL in CASE statement is wrong. Here is a small demo
declare #FutureEffectiveDate as date = null
Select Case #FutureEffectiveDate when NULL then 1 else 0 end
The above query will result 0. Because the above CASE statement validates the input expression like #FutureEffectiveDate = NULL which will fail. NULL should be compared using IS operator
Here is the correct way to compare NULL
SELECT CASE
WHEN #FutureEffectiveDate IS NULL THEN
CASE ..
Related
Looking to write a function where I can call from any two columns in my table, to get the resulting column in the same table.
Table: Acme
Columns: CustomerID, LastName, FirstName, EmailAdress, MailingAddress, City, State, Zip
Can I get EmailAddress from just FirstName and Last Name?
Can I get Zip from CustomerID and City?
What I have so far for EmailAddress:
CREATE FUNCTION fnEmailAddress
(#LastName varchar (255), #FirstName varchar (255))
RETURNS table
RETURN (SELECT EmailAddress
FROM Acme
WHERE FirstName = #FirstName AND LastName = #LastName);
END
So the following would give me brenda.chen#gmail.com:
EXEC fnEmailAddress ('Brenda'+'Chen')
But it doesn't work :(
If I understand you correctly, you want a function that can adapt to several possibilities of input combinations, because what columns you will have ready as input might differ in each function call. If so:
Clearly, your function must have as many parameters as column possibilities you have, i.e. for any column that can possibly become a "column 1" or "column 2", add a parameter.
Since you have many parameters, you need to decide which ones are to be used, or if the inputs are irrational to begin with. For example:
What to do if you have 4 out of 4 parameters provided? Refuse to work? Or can the function adapt to that?
What if parameters 1 and 4 are provided but your function isn't meant to work with that? It's meant to work with 1 + 2 or 3 + 4 for example.
Lastly, choose one of either:
Set any unnecessary inputs to NULL, then query once with a WHERE clause that auto-passes any NULL inputs (example: WHERE COL1 = ISNULL(#PAR1, COL1) AND COL2 = ISNULL(#PAR2, COL2), needs more logic for NULLable columns),
Or, utilizing IF/ELSE commands to switch between several different queries (different queries is probably maintenance headache, so avoid when possible).
Below is an example based on something I already have, although it uses the IF/ELSE approach. Try to do NULL/ISNULL instead:
CREATE FUNCTION [dbo].[SomeWackoName]
(#FirstArm tinyint, #SecondArm tinyint, #FirstLeg tinyint, #SecondLeg tinyint)
RETURNS #Results TABLE(
[Value] varchar(60)
) AS
BEGIN
-- For input, we need both arms, or both legs. It is acceptable to provide three inputs.
-- It is unacceptable to provide all four inputs (confusing), or failing to provide any full pair (like one input, or two unpaired inputs).
DECLARE #Validator tinyint = CASE WHEN #FirstArm + #SecondArm IS NULL THEN 0 ELSE 1 END + CASE WHEN #FirstLeg + #SecondLeg IS NULL THEN 0 ELSE 2 END
IF #Validator NOT BETWEEN 1 AND 2 RETURN --THROW 50000, 'INCORRECT INPUT WHYYYYYYYYYYYYYYY WHYYYYYYYYYYYYYYYYYY', 0;
-- Depending on input provided, decide how to behave.
IF #Validator = 1
INSERT INTO #Results SELECT 'I GOT THE ARMS. POPULATE WITH LEGS!!'
ELSE IF #Validator = 2
INSERT INTO #Results SELECT 'I GOT THE LEGS. POPULATE WITH ARMS!!'
-- Return results.
RETURN
END
CREATE FUNCTION fnEmailAddress
(
#LastName varchar (255)=null, #FirstName varchar (255)=null,
#streetNumber varchar(255)=null,#streetname varchar (255)=null
)
RETURNS table
Select case when #firstname+#lastname is not null then EMail Else LastName End Column3
from person
where
(
case when #firstname+#lastname is not null
then case when FirstName=#firstname and LastName=#lastname
then 1 else 0 End
when #StreetNumber+#streetname is not null
then case when StreetName=#Streetname and StreetNumber=#streetNumber
then 1 Else 0 End
End
)=1
The function should have 4 input parameters, pass first two or the last two parameters only.
CASE in the select statement will get 'Email' or 'LastName' depending upon input parameter null or not.
Similarly, CASE in the where statement will choose which filter should apply depending upon input parameter null or not, and results will be fetched based on that.
I have a basic question of SQL
What does the IsNull()=0 means in this code?
As far as I know, = assigns a value, but in this case it is used inside a Where statement.
Hope somebody can explain it to me :)
Where vol.espe_codigo = matriz.espe_codigo
And IsNull(costo_gerencias.plde_codigo,0) = 0
isnull(firstvalue, secondvalue) tests if the value for firstvalue is NULL or not. When the value is not null than this value is returned, but when the value is null than the value of secondvalue is returned.
Examples :
declare #test varchar(100) = 'abc'
declare #test2 varchar(100) = '123'
isnull(#test, #test2) will return 'abc'
second example :
declare #test varchar(100) = null
declare #test2 varchar(100) = '123'
isnull(#test, #test2) will return '123'
third example :
declare #test varchar(100) = ''
declare #test2 varchar(100) = '123'
isnull(#test, #test2) will return ''
I use it most to check on varchar columns, since they may contain an empty string which is not the same as NULL, but in a DataGridView or TextBox you cannot see the difference.
It means that if costo_gerencias.plde_codigo is null, it will return 0 or whatever the second argument is to the ISNULL function. In this case, the WHERE clause is basically saying "where vol.espe_codigo is equal to matriz.espe_codigo and costo_gerencias.plde_codigo is null or equal to zero."
IsNull() is a function which takes two input values of any type; however the second must be implicitly convertible to the type to the 1st parameter. (See link)
The database engine evaluates the first parameter and if it's null, the system returns the second parameter. If the 1st parameter is not null, it returns the value of the first parameter.
So in your case the engine will return the value 0 if plde_codigo is null. If not null it returns the value of plde_codigo passed in.
This in turn means your result set will only contain records which have a NULL or 0 value in plde_Codigo; in addition to other limiting criteria.
It could also be written as:
(costo_gerencias.plde_codigo is NULL OR costo_gerencias.plde_codigo = 0)
or to be more database agnostic:
coalesce(costo_gerencias.plde_codigo,0)=0
But in this case plde_codigo must be numeric; whereas it could be text when using isNull()
Given the following stored procedure, I'd like to be able to shift my input parameters so if the first parameter isn't a valid date, the 2 other parameters that are dates are shifted as the input. I also want to have the current day be used if there are no input parameters to my stored procedure. What is the best way to do it? I'm using SQL Server 2008 r2.
Create PROCEDURE [dbo].[p_qIMO_TEST_2]
#i_InstrumentID VARCHAR(15) = NULL,
#i_DateLow DATETIME = '20090101',
#i_DateHigh DATETIME = '20291231'
AS
IF #i_DateLow IS NULL SET #i_DateLow = CONVERT(DATETIME,CONVERT(DATE,GETDATE()))
IF #i_DateHigh IS NULL SET #i_DateHigh = CONVERT(DATETIME,CONVERT(DATE,GETDATE()))
SELECT * FROM
(
SELECT
out_interface_id,
msg_id,
CAST(xml_msg as XML).value(
'(//InstrumentID)[1]','nvarchar(15)') AS InstrumentID,
msg_type,
xml_msg,
CAST(xml_msg AS XML) as [Quick_XML],
date_received,
status,
last_modified,
environment,
transaction_closed_date
FROM MyTable
WHERE msg_type IN ('ABC','DEF')
AND date_received >= #i_DateLow
AND date_received < DATEADD(DAY,1,#i_DateHigh) -- Need to add 1 to the DateHigh for
-- date range criteria to work properly (>= and <)
) x
WHERE (x.InstrumentID = #i_InstrumentID OR x.InstrumentID = NULL)
ORDER BY date_received DESC
RETURN
GO
Updated for more clarity
Basically, I want it to check if the first argument is a valid date, probably using IsDate()and if it isn't a valid date, then I know it is an InstrumentID. If it is an InstrumentID, I want to check if the next argument is there. If it is there, check if there is a 3rd argument. That would indicate that all 3 arguments are there so I know it is a valid InstrumentID with start and end dates. If there is only a valid first argument, I want it to use the current date for the 2nd and 3rd arguments. I know it's convoluted but that's what I've been asked to do. There is no front end app, so I have to do it in a T-SQL stored procedure.
you can use the ISDATE function to check if the first parameter is a valid date. If it is not use it as a InstrumentId. For second requirement, Make the date parameter defaulted to NULL and in the SP check ISNULL(2ndpara) , ISNULL(3rdPara) That should work.
I have a problem with my report from a live query. In my report i have 5 parameters that allow to pick a date range #DateFrom and #DateTo and 3 parameters which should allow to select specific attributes:
#salesid, #batch, #serial
I want to make date range parameters mandatory and that's working without any problems but last 3 parameters: #salesid, #batch, #serial should be optional.
But they do not work as they should. Last 3 parameters should let you type in value which would work as filters. But when I pick date and one of those parameters Im getting the entire value of the query instead of selected values.
In parameters properties I slected "Allow blank value (" ")" option and just in case I defined a default value as blank.
That's how conditions in my query looks like:
where
st.custaccount <> 'number%'
and datepart(year,st.CREATEDDATETIME) >= 2012
and (ita.VENDORCODE like'producer1' or ita.vendorcode like 'producer2')
and st.createddatetime >= #FromDate
and st.createddatetime <= #ToDate
or (st.salesid = #salesid or #salesid is null)
or (itd.INVENTBATCHID = #batch or #batch is null)
or (itd.INVENTSERIALID = #serial or #serial is null)
Theoretically it should work but... Well but in practice it's not.
How to set a condition to get the desired effect? I couldn't find anything helpful so far. If any of you know something useful please give me some clues.
You need to change your final 3 or statements to and statements.
At the moment, your query is essentially checking for data items that match your criteria OR that any of those final three parameters is matched/null. This means that even if you data isn't the date range etc it will still be returned:
where st.custaccount <> 'number%'
and st.CREATEDDATETIME >= '20120101' -- Try not to use functions in your WHERE clause, as this stops indexes from working, making your query less efficient.
and ita.VENDORCODE in('producer1', 'producer2') -- IN has the same effect.
and st.createddatetime between #FromDate and #ToDate -- BETWEEN is an inclusive date range. Equivalent to >= and <=
and (st.salesid = #salesid
or #salesid is null
)
and (itd.INVENTBATCHID = #batch
or #batch is null
)
and (itd.INVENTSERIALID = #serial
or #serial is null
)
This question would be addendum on the last answer in T-SQL stored procedure that accepts multiple Id values
I am passing a few list of ids as a parameter to a stored procedure. Each of them default to null if no data is sent in. For instance, I want food products with ids 1, 2, 5, 7, 20 returned by my stored procedure. I also send in a list of color ids, and production location ids. I am passing in a comma delimited list of these ids. Similar to the last answer in the question referenced above, I create a temp table with the data from each of the parameters. I then want to have a select statement that would be something like this:
SELECT * FROM Candies
INNER JOIN #TempColors
ON Candies.ColorsID = #TempColors.ColorID
INNER JOIN Locations
ON Candies.LocationID = Locations.LocationID
This only works when the parameters are populated and LEFT OUTER JOINS will not filter properly. What is the way to filter while accepting null as a valid parameter?
You could use
some join condition OR #param IS NULL
in your join, it would return all results if a null was supplied - though as far as I can see you don't specify what behaviour you want when null is passed
(when I say param I mean temp table column doing this on my phone and it's not easy ;))
Edit:
This one worked for me:
http://sqlfiddle.com/#!3/c7e85/26
e.g.
-- Assume this is your values string which is populating the table
DECLARE #Values varchar(50)
-- Your code to populate the table here: assume the string is NULL when no values are passed
INSERT INTO #TempColors BLAH BLAH...
-- Select statement
SELECT * FROM Candies
LEFT JOIN #TempColors
ON Candies.ColorsID = #TempColors.ColorID
WHERE 1 = CASE
WHEN Candies.ColorsID IS NULL AND #Values IS NULL THEN 1
WHEN Candies.ColorsID IS NOT NULL AND #Values IS NOT NULL THEN 1
ELSE 0
END
This way the NULLs will be filtered out with a NON-NULL parameter, but kept in for a NULL parameter