SQL Server function return unexpected result - sql

I have a function to check if user is authenticated but the result is not correct :
CREATE FUNCTION [dbo].[IsAuthorized]
(#UserToken nvarchar(250),
#UserCode nvarchar(250))
RETURNS int
AS
BEGIN
IF (SELECT [User].UserId
FROM [User]
INNER JOIN UserLogin ON [User].UserId = UserLogin.UserId
WHERE [User].UserCode = #UserCode
AND UserLogin.UserToken = #UserToken
AND UserLogin.UserTokenExpiration > GETDATE()) > 0
RETURN 1;
IF NOT EXISTS (SELECT [User].UserId
FROM [User]
WHERE [User].UserCode = #UserCode)
RETURN -201; -- User does not exist
IF NOT EXISTS (SELECT [User].UserId
FROM [User]
INNER JOIN UserLogin ON [User].UserId = UserLogin.UserId
WHERE [User].UserCode = #UserCode
AND UserLogin.UserToken = #UserToken)
RETURN -202; -- Token is not valid
IF EXISTS (SELECT [User].UserId
FROM [User]
INNER JOIN UserLogin ON [User].UserId = UserLogin.UserId
WHERE [User].UserCode = #UserCode
AND UserLogin.UserToken = #UserToken
AND UserLogin.UserTokenExpiration < GETDATE())
RETURN -203; -- Token expired
RETURN 0;
END
When I try to run:
DECLARE #UserCode nvarchar(250) = N'7C6898E2-0529-4C3F-B4B2-FA69087CDF4A';
DECLARE #UserToken nvarchar(250)= N'DE3B193D-65BC-4F75-970A-932C9F825D81';
SELECT dbo.IsAuthorized(#UserCode,#UserToken) as FunctionResult
SELECT *
FROM [User]
INNER JOIN UserLogin ON [User].UserId = UserLogin.UserId
WHERE
[User].UserCode = #UserCode
AND UserLogin.UserToken = #UserToken
AND UserLogin.UserTokenExpiration > GETDATE()
I get this result:
==> the function does not find the line
-201
==> the query finds a line
4 7C6898E2-0529-4C3F-B4B2-FA69087CDF4A Ahmed ALOUI TROY aloui.ahmed#wanadoo.fr 0 0 1 0 /Ressources/img/aloui.jpg 4 73828562FADE36DD6774C6854F52965C CC6CA2373C2240743D051352BC3AF3C0 DE3B193D-65BC-4F75-970A-932C9F825D81 2016-11-25 16:02:12.083
Any clues?

The difference is that in your function, you are running a SELECT and comparing the result of that select to see whether it is greater than zero, (which will not work), whereas in your raw SELECT without using the function, you are just returning the whole record(s) if found.
You probably want this in your function:
IF (SELECT COUNT([User].UserId)
FROM [User]
INNER JOIN UserLogin ON [User].UserId = UserLogin.UserId
WHERE [User].UserCode = #UserCode
AND UserLogin.UserToken = #UserToken
AND UserLogin.UserTokenExpiration > GETDATE()) > 0
RETURN 1;
which will count the number of records returned by your SELECT and if the number is greater than zero, will return a value of 1 from the function.

IF EXISTS (SELECT 1
FROM
User u
INNER JOIN UserLogin l
ON u.UserId = l.UserId
AND l.UserToken = #UserTokey
AND l.UserTokenExpiration = GETDATE()
WHERE
u.UserCode = #UserCode
)
BEGIN
RETURN 1;
END
So the way you have User.UserId > 0 should work but only if a single scalar numeric value is returned from the query. If multiple are returned it would error out. Given what you are showing that would seem to be true but perhaps you are only showing some of the results etc. Anyway, in the rest of the function you are using the perfect technique to change this too as well. Just use IF EXISTS(SELECT....).
Note it is a pet peeve of mine to include conditions to restrict a joined table in the WHERE clause instead of the ON conditions. putting them in the ON could be more optimized but your intentions are clearer as such I have modified the query to reflect that suggestion.

Related

How to extract people who have NOT transacted since the last visit

I'm supposed to be reaching users who have NOT transacted since the configured days. I'm capturing users who HAVE transacted within X days. Not quite sure how I should get the user who have NOT transacted since the last the visit
CREATE PROCEDURE [ContentPush].[GetLastVisitDateTransaction]
#DaysSinceLastVisit INT,
#TenantID UNIQUEIDENTIFIER
AS
BEGIN
DECLARE #ReturnJson NVARCHAR(MAX)
SET #ReturnJson = (
SELECT DISTINCT [D].[UserID]
FROM [dbo].[UserInfo] D WITH(NOLOCK)
INNER JOIN [Txn].[Txn] T WITH (NOLOCK) ON [D].[UserID]=[T].[UserID]
INNER JOIN [Txn].[TxnPaymentResponse] TPR WITH(NOLOCK) ON [T].[TxnID] = [TPR].[TxnID]
WHERE
[TPR].[PaymentResponseType] = 'FINAL'
AND [TPR].[PaymentResultCode] = 'approved'
AND [T].[AppTenantID] = #TenantID
AND
(
[T].[TransactionDateTime]>= DATEADD(DD, - #DaysSinceLastVisit, GETUTCDATE())
)
AND D.IsActive = 1
FOR JSON PATH)
SELECT #ReturnJson
END
You can start from the users table, and use not exists to filter out those that had a transaction within the period.
SELECT [D].[UserID]
FROM [dbo].[UserInfo] D
WHERE NOT EXISTS (
SELECT 1
FROM [Txn].[Txn] T
INNER JOIN [Txn].[TxnPaymentResponse] TPR ON [T].[TxnID] = [TPR].[TxnID]
WHERE
[D].[UserID]=[T].[UserID]
AND [TPR].[PaymentResponseType] = 'FINAL'
AND [TPR].[PaymentResultCode] = 'approved'
AND [T].[AppTenantID] = #TenantID
AND [T].[TransactionDateTime]>= DATEADD(DD, - #DaysSinceLastVisit, GETUTCDATE())
AND D.IsActive = 1
)

How to use If Else if and Else in stored procedures | SQL Server

I have created the following stored procedure where user passes input parameters to get the data. The input parameters are optional i.e. passing just one parameter should produce some data.
Here is my code.
ALTER PROC [dbo].[SPR_ECRM_CASE_INFORMATION]
#ID_APPLICATION_NO NVARCHAR(20),
#TX_ID_NO NVARCHAR(20)
AS
BEGIN
SET NOCOUNT ON
IF (#ID_APPLICATION_NO IS NOT NULL ) OR (LEN(#ID_APPLICATION_NO) > 0) AND ((#TX_ID_NO IS NOT NULL) OR (LEN(#TX_ID_NO) > 0))
BEGIN
SELECT DISTINCT
[A].[ID_APPLICATION_NO]
,[A].[TX_APPLICATION_STATUS]
FROM [VW_T_APPLICATION] As [A]
INNER JOIN [CT_L1_APPLICATION_STATUS] [L] ON [L].[TX_L1_APPLICATION_STATUS_CODE] = [A].[TX_APPLICATION_STATUS_CODE]
LEFT JOIN [VW_ST_APPLICATION_APPLICANT] [I] WITH(NOLOCK) ON [I].[ID_APPLICATION_APPLICANT] = (SELECT TOP 1
[II].[ID_APPLICATION_APPLICANT]
FROM
[ST_APPLICATION_APPLICANT] [II] WITH(NOLOCK)
WHERE [II].[ID_APPLICATION_GUID] = [A].[ID_APPLICATION_GUID]
AND [II].[CD_APPLICANT_TYPE] = 81
AND [II].[IN_ACTIVE] = 1
ORDER BY
[II].[ID_APPLICATION_APPLICANT] DESC)
LEFT JOIN [VW_ST_APPLICATION_APPLICANT] [J] WITH(NOLOCK) ON [J].[ID_APPLICATION_APPLICANT] = (SELECT TOP 1
[JJ].[ID_APPLICATION_APPLICANT]
FROM
[ST_APPLICATION_APPLICANT] [JJ] WITH(NOLOCK)
WHERE [JJ].[ID_APPLICATION_GUID] = [A].[ID_APPLICATION_GUID]
AND [JJ].[CD_APPLICANT_TYPE] = 82
AND [JJ].[IN_ACTIVE] = 1
ORDER BY
[JJ].[ID_APPLICATION_APPLICANT] DESC)
WHERE
[I].[TX_ID_NO] = #TX_ID_NO AND [J].[TX_ID_NO] = #TX_ID_NO
AND
[A].[TX_APPLICANT_ID_NO] = #ID_APPLICATION_NO
AND
[A].[IN_ACTIVE] = 1
END
ELSE IF (#ID_APPLICATION_NO IS NOT NULL) OR (LEN(#ID_APPLICATION_NO) > 0)
BEGIN
SELECT DISTINCT
[A].[ID_APPLICATION_NO]
,[A].[TX_APPLICATION_STATUS]
FROM [VW_T_APPLICATION] As [A]
INNER JOIN [CT_L1_APPLICATION_STATUS] [L] ON [L].[TX_L1_APPLICATION_STATUS_CODE] = [A].[TX_APPLICATION_STATUS_CODE]
LEFT JOIN [VW_ST_APPLICATION_APPLICANT] [I] WITH(NOLOCK) ON [I].[ID_APPLICATION_APPLICANT] = (SELECT TOP 1
[II].[ID_APPLICATION_APPLICANT]
FROM
[ST_APPLICATION_APPLICANT] [II] WITH(NOLOCK)
WHERE [II].[ID_APPLICATION_GUID] = [A].[ID_APPLICATION_GUID]
AND [II].[CD_APPLICANT_TYPE] = 81
AND [II].[IN_ACTIVE] = 1
ORDER BY
[II].[ID_APPLICATION_APPLICANT] DESC)
LEFT JOIN [VW_ST_APPLICATION_APPLICANT] [J] WITH(NOLOCK) ON [J].[ID_APPLICATION_APPLICANT] = (SELECT TOP 1
[JJ].[ID_APPLICATION_APPLICANT]
FROM
[ST_APPLICATION_APPLICANT] [JJ] WITH(NOLOCK)
WHERE [JJ].[ID_APPLICATION_GUID] = [A].[ID_APPLICATION_GUID]
AND [JJ].[CD_APPLICANT_TYPE] = 82
AND [JJ].[IN_ACTIVE] = 1
ORDER BY
[JJ].[ID_APPLICATION_APPLICANT] DESC)
WHERE
[A].[TX_APPLICANT_ID_NO] = #ID_APPLICATION_NO
AND
[A].[IN_ACTIVE] = 1
END
ELSE IF (#TX_ID_NO IS NOT NULL) OR (LEN(#TX_ID_NO) > 0)
BEGIN
SELECT DISTINCT
[A].[ID_APPLICATION_NO]
,[A].[TX_APPLICATION_STATUS]
FROM [VW_T_APPLICATION] As [A]
INNER JOIN [CT_L1_APPLICATION_STATUS] [L] ON [L].[TX_L1_APPLICATION_STATUS_CODE] = [A].[TX_APPLICATION_STATUS_CODE]
LEFT JOIN [VW_ST_APPLICATION_APPLICANT] [I] WITH(NOLOCK) ON [I].[ID_APPLICATION_APPLICANT] = (SELECT TOP 1
[II].[ID_APPLICATION_APPLICANT]
FROM
[ST_APPLICATION_APPLICANT] [II] WITH(NOLOCK)
WHERE [II].[ID_APPLICATION_GUID] = [A].[ID_APPLICATION_GUID]
AND [II].[CD_APPLICANT_TYPE] = 81
AND [II].[IN_ACTIVE] = 1
ORDER BY
[II].[ID_APPLICATION_APPLICANT] DESC)
LEFT JOIN [VW_ST_APPLICATION_APPLICANT] [J] WITH(NOLOCK) ON [J].[ID_APPLICATION_APPLICANT] = (SELECT TOP 1
[JJ].[ID_APPLICATION_APPLICANT]
FROM
[ST_APPLICATION_APPLICANT] [JJ] WITH(NOLOCK)
WHERE [JJ].[ID_APPLICATION_GUID] = [A].[ID_APPLICATION_GUID]
AND [JJ].[CD_APPLICANT_TYPE] = 82
AND [JJ].[IN_ACTIVE] = 1
ORDER BY
[JJ].[ID_APPLICATION_APPLICANT] DESC)
WHERE
[I].[TX_ID_NO] = #TX_ID_NO OR [J].[TX_ID_NO] = #TX_ID_NO
AND
[A].[IN_ACTIVE] = 1
END
ELSE
BEGIN
RETURN NULL
END
END
I have used if else if and else condition but it fails to return the data.
DECLARE #A NVARCHAR(30)
DECLARE #B NVARCHAR(20)
SET #A = 'C192'
SET #B = 'ABC'
EXEC SPR_ECRM_CASE_INFORMATION #A, ''
EXEC SPR_ECRM_CASE_INFORMATION '', #B
EXEC SPR_ECRM_CASE_INFORMATION '', ''
Firstly, the else if condition fails and my code here is redundant. Is there any better way to optimize this. Your help is appreciated.
If you want to optimize for performance, you can create a single procedure for each query. Each will get it's own query plan. You can call each in this procedure.
Looking at each query, I see the sub-query in the ON clause. I also see NOLOCK which I normally consider a bad sign. Also see the DISTINCT. These are all clues that the query can be simplified. SQL Server will then be more likely to optimize the query. (You can ask for the same thing in more than one way. The more complex the logic, the less likely SQL will find the best plan.)
If the data in [VW_T_APPLICATION] is already distinct, then get rid of DISTINCT and use EXISTS instead of LEFT JOINS - one EXISTS for 81 and one for 82. (I'm assuming you are not going to need columns from these.)
The use of "VW" implies the use of views. The view can hide a lot of logic that gets in the way of a simple query.
Is this part (your first IF) correct in terms of bracketing? It will run if #ID_Application_No is NOT NULL, regardless of whether #TX_ID_No has a value.
IF (#ID_APPLICATION_NO IS NOT NULL ) OR (LEN(#ID_APPLICATION_NO) > 0) AND ((#TX_ID_NO IS NOT NULL) OR (LEN(#TX_ID_NO) > 0))
I'm guessing you want either
a) brackets around the ID_Application_No part e.g.,
IF ((#ID_APPLICATION_NO IS NOT NULL ) OR (LEN(#ID_APPLICATION_NO) > 0)) AND ((#TX_ID_NO IS NOT NULL) OR (LEN(#TX_ID_NO) > 0))
or b) want it to be an AND (this is my most likely guess) e.g.,
IF (#ID_APPLICATION_NO IS NOT NULL ) AND (LEN(#ID_APPLICATION_NO) > 0) AND ((#TX_ID_NO IS NOT NULL) OR (LEN(#TX_ID_NO) > 0))
Note you can simplify some things e.g., when you have IF (#ID_APPLICATION_NO IS NOT NULL) OR (LEN(#ID_APPLICATION_NO) > 0), the first part will always be true if the second part is true. In those cases, you can use only IF (#ID_APPLICATION_NO IS NOT NULL) and it will give the same results.

SQL Server stored procedure store multiple rows of SELECT statement result into single variable

I have a query with a SELECT statement that will return 2 or more rows as a result. How can I store these rows of data into a variable? Because I need the variable to check whether any of the rows is empty/null. How can I achieve this?
So far I've done this:
BEGIN
SELECT
#AINum = ISNULL(so.U_SI7_DPDocNum, 0), #soDocNum = so.DocNum
FROM
DLN1 doline
INNER JOIN
ORDR so ON doline.BaseRef = so.DocNum
WHERE
doline.DocEntry = #docEntry
WHILE(#AINum IS NOT NULL)
BEGIN
IF(#AINum <= 0)
BEGIN
SELECT #errCode = 003;
RETURN;
END
END
END
UPDATED query using EXISTS
SELECT #errCode = 003
WHERE NOT EXISTS (SELECT so.U_SI7_DPDocNum
FROM DLN1 doline
INNER JOIN ORDR so ON doline.BaseRef = so.DocNum
WHERE doline.DocEntry = #docEntry)
RETURN;
The #AINum will have to store multiple rows of data from the SELECT statement result. #errCode is an output variable.
Thank you.
-- initialize to 0
SELECT #errCode = 0;
-- assign value of 003 if it the DPDocNum is NULL or < 0
SELECT #errCode = 003
FROM DLN1 doline
INNER JOIN ORDR so ON doline.BaseRef = so.DocNum
WHERE doline.DocEntry = #docEntry
AND (so.U_SI7_DPDocNum IS NULL OR so.U_SI7_DPDocNum <= 0)

Join does not return value when one of the tables in the joins does not have the record

I have this stored procedure. My problem is when record is not existed in Agency Table is does not return anything. I would like to say even if there is no record in Agency still return so I have added the left outer JOIN for Agency and _Agency = IsNull(U._Agency,'')
At the top. But still does not return value. It is returning value when I take A._IsActive = 1
At the last line out.
What should I do id A._IsActive
Has no value still return. I tried same ISNull but not working.
declare #Username VARCHAR(50)
, #Password VARCHAR(50)
set #Username = 'admin'
set #Password = 'password2'
SELECT U.Username
,_Partner = u.AID
,_Agency = IsNull(U._Agency,'')
, UR._Role
,R.Name
FROM [PartnerPortal].[dbo].[User] AS U
left outer JOIN [PartnerPortal].[dbo].Agency AS A
ON U._Agency = A._IdxIdentity
JOIN [PartnerPortal].[dbo].User_Role AS UR
ON U._IdxIdentity = UR._User
JOIN [PartnerPortal].[dbo].[Role] AS R
ON UR._Role = R._IdxIdentity
WHERE (Username = #Username)
AND [Password] =#Password
AND U._IsActive = 1
AND A._IsActive = 1
AND UR._IsActive = 1
AND R._IsActive = 1
The problem is here:
...
left outer JOIN [PartnerPortal].[dbo].Agency AS A
ON U._Agency = A._IdxIdentity
...
WHERE ... A._IsActive = 1
When Agency has no matching row, all columns returned for that table are null, but you are requiring that _IsActive be 1, which will not be true for left joins.
Move that condition into the ON condition of the left join:
...
left outer JOIN [PartnerPortal].[dbo].Agency AS A
ON U._Agency = A._IdxIdentity
AND A._IsActive = 1
...
WHERE ...
Now the condition will apply if there's a matching row, or you'll get the all-null row if there's no matching row.

Select from multiple tables with multiple where clauses

I am trying to write a stored procedure that will give a count of all the cases in a table that are not deleted, grouped by a CaseStatusID in another table, but only the cases that have a CaseStatusID that also isn't deleted. I also want it to return any CaseStatusID that does not have case related to it (i.e. a count of 0)
So far I have tried
Create Table #tCaseStatus (CaseStatusID int,CaseStatusDesc varchar(200) )
declare #NofCases int
declare #CaseStatusID int
declare #CaseStatusDesc varchar(200)
BEGIN
INSERT #tCaseStatus
Select CaseStatusID, CaseStatusDesc From dbo.CaseStatus Where IsDeleted = 0
Select #NofCases = Count(*) From #tCaseStatus
While (#NofCases > 0)
Begin
Select Top (1) #CaseStatusID = CaseStatusID, #CaseStatusDesc = CaseStatusDesc from #tCaseStatus
SELECT CaseStatusDesc, Count(CaseID) AS CountCases
FROM Cases inner join #tCaseStatus on Cases.CaseStatusID = #tCaseStatus.CaseStatusID
WHERE (IsDeleted = 0) AND Cases.CaseStatusID = #CaseStatusID
Group by #tCaseStatus.CaseStatusDesc
Set #NofCases = #NofCases - 1
Delete Top(1) from #tCaseStatus
End
END
AND
This returns the correct cases but excludes any of the CaseStatusDesc that have a count of 0
SELECT CaseStatus.CaseStatusDesc, COUNT(Cases.CaseID) AS CaseCount
FROM Cases FULL OUTER JOIN
CaseStatus ON Cases.CaseStatusID = CaseStatus.CaseStatusID
WHERE (CaseStatus.IsDeleted = 0) AND
(Cases.IsDeleted = 0)
GROUP BY CaseStatus.CaseStatusDesc, CaseStatus.CaseStatusID
ORDER BY CaseStatus.CaseStatusID
AND
this returns all the CaseStatusDesc's even the ones that are deleted
SELECT CaseStatus.CaseStatusDesc, COUNT(CASE
WHEN CaseStatus.IsDeleted = 0 THEN 'ok'
WHEN Cases.IsDeleted = 0 THEN 'ok'
Else null
END) AS [Case]
FROM Cases FULL OUTER JOIN CaseStatus ON Cases.CaseStatusID = CaseStatus.CaseStatusID
GROUP BY CaseStatus.CaseStatusDesc, CaseStatus.CaseStatusID
Order By CaseStatus.CaseStatusID asc
But I cant seem to get the desired results
Is this what you're after?
select
casestatus.casestatusid,
casestatusdesc,
COUNT(caseid)
from casestatus
left join cases
on casestatus.casestatusid = cases.casestatusid
and cases.isdeleted=0
where
casestatus.isdeleted=0
group by
casestatus.casestatusid,
casestatusdesc