Does this query require dynamic sql? SQL Server - sql

I want to select the parameters of a stored procedure and then check whether each parameter is null or not in a loop.
The problem I'm having is that when I want to evaluate the variable in the IF statement, it is not being evaluated correctly. I want to evaluate each parameter I stored in the table variable. I don't know the syntax for this...or maybe it isn't even possible?
Does this require Dynamic SQL? If so, when executing dynamic sql, the value of the variable will be out of scope so how do I deal with that?
I verified everything up to the IF statement works.
CREATE PROCEDURE dbo.UpdateBank
(
#BankKey [smallint] = NULL,
#Bank [varchar] (30) = NULL,
#BankCode [char] (4) = NULL,
#MasterBankCode [char] = NULL,
#Bin [char] (6) = NULL,
#WebSite [varchar] (50) = NULL,
#isActive [bit] = NULL,
#CreateDate [smalldatetime] = NULL
)
AS
SET NOCOUNT ON
SET ANSI_NULLS OFF
DECLARE #MaxRow TINYINT, #Count TINYINT
DECLARE #SPName VARCHAR (128), #CurrentRow TINYINT
SET #SPName = OBJECT_NAME(##PROCID) -- SP self-reference to find its current name
DECLARE #SPParametersList TABLE (ID INT Identity(1,1) Primary Key,
ParameterName NVARCHAR (128),
DataType NVARCHAR (128),
ParameterMode NVARCHAR (10))
CREATE TABLE #TempExec(ID INT Identity(1,1) Primary Key,
Num BIT)
INSERT INTO #SPParametersList (ParameterName, Datatype, ParameterMode)
SELECT PARAMETER_NAME,DATA_TYPE,PARAMETER_MODE
FROM INFORMATION_SCHEMA.PARAMETERS
WHERE SPECIFIC_NAME = #SPName
SET #CurrentRow = 1
SELECT #MaxRow = ISNULL(MAX(ID),0) FROM #SPParametersList
WHILE #CurrentRow <= #MaxRow
BEGIN
IF ((SELECT ParameterName FROM #SPParametersList WHERE ID = #CurrentRow) <> NULL)
BEGIN
SELECT 'Success'
SET #Count = #Count + 1
END
SELECT 'Fail'
SET #CurrentRow = #CurrentRow + 1
END
SELECT #Count
I always get 'Fail' when I run this stored proc

Change your line:
IF ((SELECT ParameterName FROM #SPParametersList WHERE ID = #CurrentRow) <> NULL)
to
IF ((SELECT ParameterName FROM #SPParametersList WHERE ID = #CurrentRow) IS NOT NULL)
You also need to initialize the #Count variable to 0:
SET #Count = 0
SET #CurrentRow = 1
SELECT #MaxRow = ISNULL(MAX(ID),0) FROM #SPParametersList

Probably the issue is in the <> NULL which should
IF EXISTS(SELECT ParameterName FROM #SPParametersList WHERE ID = #CurrentRow)
but I'm not sure what you want to achieve with that piece of code...

I suspect that this is an example of a query that can be rewritten without the use of loops/cursors (most sql is in my experience...)
Does the query below give you your desired results?
with temp as
(
SELECT '#BankKey' as ParamName
UNION
SELECT '#Bank'
)
SELECT COUNT(*) as myCount
FROM INFORMATION_SCHEMA.PARAMETERS as isp
LEFT JOIN temp as t
ON t.ParamName = isp.PARAMETER_NAME
WHERE SPECIFIC_NAME = #SPName AND t.ParamName is null
You should try to avoid using Loops/Cursors as much as possible. SQL Server (and most DBMSs in general) are excellent at performing Set based operations and terrible at performing row based operations (loops/cursors).

(1) "I always get 'Fail' when I run this stored proc": you have forget the ELSE branch
IF ((SELECT ParameterName FROM #SPParametersList WHERE ID = #CurrentRow) /*<>*/ IS NOT NULL)
BEGIN
SELECT 'Success'
SET #Count = #Count + 1
END
ELSE -- << here
BEGIN
SELECT 'Fail'
END
SET #CurrentRow = #CurrentRow + 1
(2) To count all not null parameters:
SELECT #Count=COUNT(*)
FROM #SPParametersList a
WHERE a.ParameterName IS NOT NULL
To count all null parameters:
SELECT #Count=COUNT(*)
FROM #SPParametersList a
WHERE a.ParameterName IS NULL
Note: if you want to test for NULL / NOT NULL you should use column/#variable IS [NOT] NULL operator and SET ANSI_NULLS must be ON: SET ANSI_NULLS ON.

Related

Same SQL Server query taking different times to execute (where statement same but syntax is different)

The two queries shown here are taking different times to execute. Both have the same WHERE condition and difference is only that where condition syntax is different.
The second query is generated from dynamic query.
Let me know if you know reason for the same.
And also let me know which one is best for performance.
Query #1 (taking 1.5 second to execute)
DECLARE #caseDetailId VARCHAR(MAX) = '16',
#patientId VARCHAR(8000) = NULL,
#isActive NVARCHAR(4000) = NULL,
#description NVARCHAR(4000) = NULL,
#clientId VARCHAR(8000) = '1021',
#machineId VARCHAR(8000) = NULL,
#oldSystemId VARCHAR(8000) = NULL,
#isDeleted NVARCHAR(4000) = NULL,
#userId INT,
#langId VARCHAR(10) = NULL,
#page INT = 0,
#size INT = 0,
#orderBy VARCHAR(400) = NULL
--Query 1
SELECT *
FROM CaseDetail
WHERE 1 = 1
AND (#isDeleted IS NULL OR [IsDeleted] = #isDeleted)
AND (#clientId IS NULL OR [ClientId] = #clientId)
AND (#caseDetailId IS NULL OR [CaseDetailId] IN (SELECT id
FROM dbo.Fnsplit(#caseDetailId,',')))
AND (#patientId IS NULL OR [PatientId] IN (SELECT id
FROM dbo.Fnsplit(#patientId,',')))
AND (#isActive IS NULL OR [IsActive] IN (#isActive))
AND ((#description IS NULL )
OR (#description IS NOT NULL AND [Description] LIKE '%'+#description+'%'))
Query #2 (taking 0.016 second to execute):
DECLARE #caseDetailId VARCHAR(MAX) = '16',
#patientId VARCHAR(8000) = NULL,
#isActive NVARCHAR(4000) = NULL,
#description NVARCHAR(4000) = NULL,
#clientId VARCHAR(8000) = '1021',
#machineId VARCHAR(8000) = NULL,
#oldSystemId VARCHAR(8000) = NULL,
#isDeleted NVARCHAR(4000) = NULL,
#userId INT,
#langId VARCHAR(10) = NULL,
#page INT = 0,
#size INT = 0,
#orderBy VARCHAR(400) = NULL
--Query 2
SELECT *
FROM CaseDetail
WHERE 1 = 1
AND CaseDetail.CaseDetailId IN (SELECT Id FROM dbo.Fnsplit(#CaseDetailId,','))
AND CaseDetail.ClientId IN (SELECT Id FROM dbo.Fnsplit(#clientId,','))
You must know this notes:
OR in the WHERE clauser is a very bad practice.
Compare a value with NULL had affectt on performance.
Sub Query with IN has very heavy overhead to performace.
The OR clause in the WHERE condition is a very bad practise in general with a few exceptions.
The SQL Optimizer is unable to find a good plan to use an index, so, it has to do a full table scan (read the full table).
So, both provided queries have this difference and in the second case is when you report a good speed. Try to reformulate the first query without the OR conditions in the WHERE and should fix the performance issue.

Can I reuse SQL or use some type of dynamic join for this stored procedure?

I have a GetUsers stored procedure as shown below. If a #GroupId or #GroupName parameter is provided, then I need to beef up the stored procedure to ensure that only users belonging to a matching group are returned.
I've created a special table variable named #GroupUserMatches which contains a UserId/GroupId association for matching user groups. I could essentially copy/paste the SQL from the ELSE block into the IF block and update it a bit to accommodate a join to #GroupUserMatches, but I was wondering if there is a more elegant way to do this?
One concern is that I'd like to avoid having to make updates to the same SQL select in 2 different blocks in the same stored procedure. If no #GroupId or #GroupName are provided, then the user list returned should be completely unfiltered by group.
ALTER PROCEDURE [dbo].[GetUsers]
#DomainId uniqueidentifier = NULL,
#DomainName varchar(100) = NULL,
#NetworkUserId varchar(100) = NULL,
#UserPrincipalName varchar(100) = NULL,
#FirstName varchar(100) = NULL,
#LastName varchar(100) = NULL,
#EmailAddress varchar(255) = NULL,
#GroupId uniqueidentifier = NULL,
#GroupName varchar(500) = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE #GroupUserMatches TABLE
(
GroupId UNIQUEIDENTIFIER,
UserId UNIQUEIDENTIFIER
)
IF(#GroupId IS NOT NULL OR #GroupName IS NOT NULL)
BEGIN
INSERT INTO #GroupUserMatches (GroupId, UserId)
SELECT geue.GroupExtensionId, geue.UserExtensionId
FROM ADGroup adg
JOIN GroupExtension ge ON adg.Id = ge.ADGroupId
JOIN GroupExtensionUserExtension geue ON ge.Id = geue.GroupExtensionId
WHERE (#GroupId IS NULL OR ge.Id = #GroupId)
AND (#GroupName IS NULL OR adg.[Name] LIKE #GroupName + '%')
END
ELSE
BEGIN
SELECT
au.*,
0 HasExtendedSecurity
FROM
AllUsers au
JOIN
Domain d ON au.DomainName = d.[Name]
WHERE
(#DomainId IS NULL OR au.DomainId = #DomainId) AND
(#DomainName IS NULL OR au.DomainName = #DomainName) AND
(#NetworkUserId IS NULL OR au.NetworkUserId = #NetworkUserId) AND
(#UserPrincipalName IS NULL OR au.UserPrincipalName = #UserPrincipalName) AND
(#FirstName IS NULL OR au.FirstName LIKE #FirstName + '%') AND
(#LastName IS NULL OR au.LastName LIKE #LastName + '%' ) AND
(#EmailAddress IS NULL OR au.Email LIKE #EmailAddress + '%')
END
END

SQL: search for null date

I have a select procedure that search my Members table.
The Members table has:
ID – Name – Deleted
1 – Marcus – NULL
2 – Regina – 2014-03-26 09:33:00.000
Being: ID a PK int no NULL, Name a varchar(250) and Deleted a datetime NULL.
Here’s my procedure:
CREATE PROCEDURE dbo.SELECT_MEMBERS
#ID int = NULL,
#Name varchar(250)= NULL,
#Deleted datetime =NULL
AS
BEGIN
IF #Deleted = ''
BEGIN
SET #Deleted = NULL
END
ELSE
BEGIN
SET #Deleted = #Deleted
END
SET NOCOUNT ON;
SELECT ID, Name, Deleted
FROM dbo.Members WHERE
(#ID IS NULL OR #ID = ID)
AND (#Name IS NULL OR #Name = Name)
AND (#Deleted IS NULL OR #Deleted = Deleted)
END
GO
In order to search for the members that have no deleted date (if they have a deleted date means that they are no active members) I do:
EXEC dbo.SELECT_MEMBERS #Deleted = ''
But this is not working as I get the row with a deleted date inserted.
What am I doing wrong?
Thanks a lot!!
CREATE PROCEDURE dbo.SELECT_MEMBERS
#ID int = NULL,
#Name varchar(250)= NULL,
#Deleted datetime =NULL
AS
BEGIN
SET NOCOUNT ON;
SELECT ID, Name, Deleted
FROM dbo.Members
WHERE ID = ID
AND ISNULL(#Name, '') = ISNULL(Name, '')
AND ISNULL(#Deleted, '') = ISNULL(Deleted, '')
END
GO
It seems that you want an empty/NULL value for #Deleted date to behave differently from a NULL value for #ID and #Name: you want the actual comparison for #Deleted to be true only when the value is NULL.
I think you want something like this:
CREATE PROCEDURE dbo.SELECT_MEMBERS
#ID int = NULL,
#Name varchar(250)= NULL,
#Deleted datetime = NULL
AS
BEGIN
SELECT ID, Name, Deleted
FROM dbo.Members
WHERE (#ID IS NULL OR #ID = ID) AND
(#Name IS NULL OR #Name = Name) AND
((#Deleted IS NULL OR #Deleted = Deleted or #Deleted = '' and Deleted is null)
END;

Total RecordCount as OUTPUT of Paged Result Set in Stored Procedure

i have a question on stored procedures.
I try to get a page of result set and the record count of the whole set.
Each of this is working on it's own, but I'm unable to combine it:
ALTER PROCEDURE dbo.pagingSCP
#PageStart INT,
#PageSize INT,
#RecordCount INT OUTPUT
AS
BEGIN
WITH AllRecords AS (
SELECT ROW_NUMBER() OVER (ORDER BY MATNR)
AS Row, viewStyleColorInModul.*
FROM viewStyleColorInModul WHERE SPRAS = 'D'
) SELECT * FROM AllRecords WHERE Row between
#PageStart and #PageStart + #PageSize
END
(50 row(s) returned)
#RecordCount = 0
#RETURN_VALUE = 0
Finished running [dbo].[pagingSCP].
ALTER PROCEDURE dbo.pagingSCP
#PageStart INT,
#PageSize INT,
#RecordCount INT OUTPUT
AS
BEGIN
WITH AllRecords AS (
SELECT ROW_NUMBER() OVER (ORDER BY MATNR)
AS Row, viewStyleColorInModul.*
FROM viewStyleColorInModul WHERE SPRAS = 'D'
) SELECT #RecordCount = Count(*) From AllRecords
END
No rows affected.
(0 row(s) returned)
#RecordCount = 43770
#RETURN_VALUE = 0
Finished running [dbo].[pagingSCP].
Is is now somehow possible to get the 50 Rows and the total Recordcount
within the single query?
Thanks in advance.
ALTER PROCEDURE dbo.pagingSCP
#PageStart INT,
#PageSize INT,
#RecordCount INT OUTPUT
AS
BEGIN
-- get record count
WITH AllRecords AS (
SELECT viewStyleColorInModul.*
FROM viewStyleColorInModul WHERE SPRAS = 'D'
) SELECT #RecordCount = Count(*) From AllRecords;
-- now get the records
WITH AllRecords AS (
SELECT ROW_NUMBER() OVER (ORDER BY MATNR)
AS Row, viewStyleColorInModul.*
FROM viewStyleColorInModul WHERE SPRAS = 'D'
) SELECT * FROM AllRecords
WHERE Row between #PageStart and #PageStart + #PageSize;
END
You have two distinct queries, therefor eyou run two distinct SELECT and let the SQL optimizer optimize each individually. Even if trying to get both queries in one SELECT is possible, is highly counterproductive and sub-optimal.
As a side note, in the client code any output parameter is available only after iterating all results returned.
Here is the guts of a paging proc we used all the time. It works by first dumping all the matching records into a temp table (WHERE SPRAS = 'D').
It then selects from the temp table, only the records from page X of Y. It also includes the total records of the original selection (WHERE SPRAS = 'D').
ALTER PROCEDURE [dbo].[spSelectTempUsers]
#Page int = 0,
#NumPerPage int = 1
AS
SET NOCOUNT ON
CREATE TABLE #TempData
(
[RowId] [int] identity(1,1) ,
[UserId] [int] ,
[FirstName] [varchar](50) ,
[LastName] [varchar](50) ,
[Email] [varchar](255) ,
[SPRAS] [varchar](36)
)
INSERT INTO #TempData
(
[UserId] ,
[FirstName] ,
[LastName] ,
[Email] ,
[SPRAS]
)
SELECT
[UserId] ,
[FirstName] ,
[LastName] ,
[Email] ,
[SPRAS]
FROM viewStyleColorInModul
WHERE [SPRAS] = 'D'
DECLARE #Count int
DECLARE #Pages int
DECLARE #i int
DECLARE #j int
IF #Page < 1 SET #Page = 1
SET #Count = (SELECT COUNT(RowId) FROM #TempData)
SET #Pages = #Count / #NumPerPage
IF (#Pages * #NumPerPage) < #Count SET #Pages = #Pages + 1
IF #Page > #Pages SET #Page = #Pages
SET #i = ((#Page -1) * #NumPerPage) +1
SET #j = #Page * #NumPerPage
SELECT
ISNULL(t1.UserId,'') as UserId,
ISNULL(t1.FirstName,'') as FirstName ,
ISNULL(t1.LastName,'') as LastName ,
ISNULL(t1.Email,'') as Email ,
ISNULL(t1.SPRAS,'') as SPRAS,
#Pages as Pages,
#Count as TotalRecords
FROM #TempData t1
WHERE t1.RowId >= #i AND t1.RowId <= #j
ORDER BY t1.RowId
DROP TABLE #TempData
SET NOCOUNT OFF

Selecting not null column

I have a table with varbinary(max) column and nvarchar(max) column. One of them is null and the other has a value.
I would like to return the column that has the value as a varbinary(max) column. So far I have tried this, that does not work:
SELECT
A =
CASE A
WHEN NULL THEN B
ELSE A
END
FROM Table
SELECT COALESCE(A, CAST(B As varbinary(max)))
UPDATE: In response to comments (thanks) and assuming B is the nvarchar(max) column, I have moved the CAST inside the COALESCE
Try SELECT ISNULL(A, cast(B AS varbinary(max))) FROM TABLE
Your case statement evaluates to the dreaded A = NULL:
CASE A WHEN NULL THEN B ELSE A END
Is the same as:
CASE WHEN A = NULL then B ELSE A END
One way to fix this is to use A IS NULL, like:
CASE WHEN A IS NULL THEN B ELSE A END
Or even simpler:
COALESCE(A,B)
Both the when and the coalesce will assume the data type of the first argument. To cast the result to varbinary, you can place the varbinary column first, or explicitly cast:
COALESCE(CAST(A AS VARBINARY(MAX)),B)
here is the full code of create table and insert value and apply my code and only retrieve not null value
CREATE TABLE [dbo].[SUPPLIER](
[ID] [int] IDENTITY(1,1) NOT NULL,
[SUPPLIER_NAME] [varchar](100) NOT NULL,
[ADDRESS] [varchar](150) NULL,
[CREATE_DATE] [datetime] NULL,)
INSERT INTO [MyPayrol].[dbo].[SUPPLIER]
([SUPPLIER_NAME]
,[CREATE_DATE])
VALUES
('Khaled Nabil'
,GETDATE())
declare #inumberofcolumn int
select #inumberofcolumn= count(*)
from sys.columns where OBJECT_NAME(object_id) = 'supplier'
declare #nameofcolumn varchar(100)
set #nameofcolumn =''
declare #counter int
set #counter=1
declare #colname varchar(100)
declare #statment varchar(100)
declare #value varchar(100)
while #counter <=#inumberofcolumn
begin
select #colname= COL_NAME(object_id('[dbo].[SUPPLIER]'),#counter)
declare #data table ([value] varchar(100))
--set #statment = 'select '+#colname+' from [dbo].[SUPPLIER]'
insert #data exec ('SELECT top 1 '+ #colname +' from [dbo].[SUPPLIER]')
select #value = [value] from #data
if #value is not null
begin
if #counter = 1
begin
set #nameofcolumn = #nameofcolumn + #colname
end
else
begin
set #nameofcolumn = #nameofcolumn + ','+ #colname
end
end
set #counter = #counter+1
end
execute ('select '+#nameofcolumn+' from [dbo].[SUPPLIER]')