SQL - UPDATE within a SWITCH CASE - sql-server

I'm trying to run an update based on the value of a flag sent into a procedure but it does not like the syntax of the UPDATE statement here. What's wrong with it?
CREATE PROC [dbo].[TestProc]
#ID int,
#GET_COUNT bit
AS
BEGIN
SELECT
CASE
WHEN #GET_COUNT = 1
THEN (SELECT COUNT(*) FROM [ORDERS]EMPLOYEE_ID = #ID)
WHEN #GET_COUNT = 0
THEN UPDATE ORDERS SET EMPLOYEE_ID = null WHERE EMPLOYEE_ID = #ID
END
GO

You are confusing a SELECT . . . CASE with IF. Your code looks like T-SQL, so this is probably what you intend:
IF #GET_COUNT = 1
BEGIN
SELECT COUNT(*) FROM [ORDERS] EMPLOYEE_ID = #ID;
END;
ELSE IF #GET_COUNT = 0
BEGIN
UPDATE ORDERS SET EMPLOYEE_ID = null WHERE EMPLOYEE_ID = #ID
END;
Some SQL scripting languages do use CASE for control-flow as well as expression evaluations. However, I think that IF is clearer in this context.

Related

How do I use WHILE loop in CASE statement in SQL

In 'CASE' statement in SQL we use a bool condition and get a TRUE or FALSE result. In this situation I have to use non-bool unlimited condition. But I can't...
ALTER proc [dbo].[sp_StudentList](#CreatedBy nvarchar(max))
as
begin
declare #LikedBy nvarchar(max) = (Select LikedBy from LikeStatus)
declare #TeacherRequestID int = (Select TeacherRequestID from LikeStatus where LikedBy=#CreatedBy)
declare #UserName nvarchar(max) = #CreatedBy
declare #i int = 1
declare #NumberOfRows int = (select count(*) from TeacherRequest)
select SP.StuThana, SP.StuDist, TR.StudentName,TR.StudentCode, TR.Class, TR.Subject, TR.StuGroup,TR.StuRelation, TR.Institute,TR.Status, TR.LikeStatus,
**CASE
WHEN
WHILE(#i <= #NumberOfRows)
BEGIN
#TeacherRequestID = TR.ID THEN 'Liked' Else 'Like'
set #i = #i + 1
END
END as LikeFlag**
from StudentsProfile SP join TeacherRequest TR on SP.CreatedBy=TR.CreatedBy
--sp_StudentList 'teacher1#gmail.com'
end
The technical answer to your question as posed in your title is that you can't.
declare #i int = 5;
select case when (while #i > 0 begin set #i = #i - 1 end) then 1 else 0 end;
-- Incorrect syntax near the keyword 'while'
Is your intention to just determine whether a student listed in a row likes the associated teacher? If so, then you're looking for whether an entry exists in another table, not how often it occurs. And I would tie it to sp.createdBy, not #createdBy.
select // ...,
likeFlag =
case when exists (
select 0
from likeStatus ls
where ls.likedBy = sp.createdBy
and ls.TeacherRequestId = tr.id
) then 'Liked'
else 'Like'
end
from studentsProfile sp
join teacherRequest tr on sp.createdBy = tr.createdBy
If for some reason you really only need 'Liked' based on #createdBy, then change ls.likedBy = sp.createdBy to ls.likedBy = #createdBy, but I don't see a strong use case for that.

SQL Server - update multiple records using a stored procedure

Being a super novice at this, I would like some guidance on this, please.
I need to compare two sets of data and update one set with a value. This is what I have so far.
PROCEDURE [dbo].[update_personnel_rank]
AS
DECLARE #frsid VARCHAR
DECLARE #officerid VARCHAR
DECLARE #hrrank VARCHAR
DECLARE #personnelrank VARCHAR
DECLARE #farank VARCHAR
DECLARE #rank VARCHAR(150)
SET #rank = 'Admin Spec II'
BEGIN
SET NOCOUNT ON;
SELECT
#frsid = hr.FRSID,
#officerid = p.OfficerID,
#hrrank = hr.Rank,
#personnelrank = p.Rank,
#farank = r.FA_Rank
FROM
[FireApp_REPL_DW_Data].[dbo].[MCFRSCombinedPersonnelandPimsStaff] hr
INNER JOIN
[fh_reports].[dbo].[personnel_bk] p ON p.OfficerID = hr.FRSID
INNER JOIN
[fh_reports].[dbo].[Rank_Lookup_tbl] r ON r.FA_Rank = hr.Rank
WHERE
(p.rank <> hr.Rank OR p.rank = '')
AND p.Rank = #rank
UPDATE [fh_reports].[dbo].[personnel_bk]
SET Rank = #farank
WHERE OfficerID = #officerid
END
GO
The select query returns 3 records and this stored procedure runs without any error, but it does not update the records. Since the select query returns 3 records, I think I need to change the parameter setting accordingly, but not sure how...
To #Sami's point, if you are not returning those variables, you do not need to set them and can instead just run the update:
USE [YourDatabase]
GO
SET NOCOUNT ON
GO
ALTER PROCEDURE [dbo].[update_personnel_rank]
#rank VARCHAR(150) --= 'Admin Spec II'
AS
BEGIN
IF #rank IS NULL OR #rank = ''
RAISERROR('Please enter a valid rank string.', 16, 1)
UPDATE hr
SET [Rank] = r.FA_Rank
FROM [FireApp_REPL_DW_Data].[dbo].[MCFRSCombinedPersonnelandPimsStaff] [hr]
INNER JOIN [fh_reports].[dbo].[personnel_bk] [p]
ON [p].[OfficerID] = [hr].[FRSID]
INNER JOIN [fh_reports].[dbo].[Rank_Lookup_tbl] [r]
ON [r].[FA_Rank] = [hr].[Rank]
WHERE [p].[rank] <> [hr].[Rank]
AND ([p].[Rank] = #rank OR p.[Rank] = '')
END ;
GO

SQL Server user-defined-function using WITH, IF together

I need to create a scalar-valued user defined function in SQL Server. I need to have with clause to store some intermediary tables which will produce the final return result. I also need IF .. ELSE because depending on the input parameter the query to the resutl is different. However, I can't write the code without error combinaing these two elements together. My function would be like this:
CREATE FUNCTION [dbo].[getMyValue](
#inputType int
)
RETURNS float
AS
BEGIN
DECLARE #result float
;
WITH tempTable AS
(
SELECT * from TableA
)
;
IF inputType = 1
set #result = select sum(t.result1) from tempTable
else
selecset #result = select sum(t.result2) from tempTable
return #result
END
GO
But now it complains incorrect syntax near 'if'. If I remove the with clause (and query against some actual table) it compiles, or if I remove IF statements it also compiles. So how can I make them work together?
You cannot use IF like this in the context of an SQL query. Try using the following instead:
DECLARE #result float, #result1 float, #result2 float
WITH tempTable AS
(
SELECT * from TableA
)
SELECT #result1 = sum(case when #inputType = 1 then t.result1 else 0 end),
#result2 = sum(case when #inputType = 2 then t.result2 else 0 end)
FROM tempTable
IF #inputType = 1
SET #result = #result1
ELSE
SET #result = #result2
RETURN #result

UPDATE from a table in SQL Native stored procedure (Hekaton)

I'm migrating a queue in disk to in memory SQL Server 2016 to implement a queue.
This is my queue format:
CREATE TABLE dbo.SimpleQueue
(
MsgId BIGINT NOT NULL PRIMARY KEY NONCLUSTERED IDENTITY(1, 1),
Payload VARCHAR(7500) NOT NULL,
IsDeleted BIT NOT NULL
) WITH (MEMORY_OPTIMIZED=ON)
GO
This is my Enqueue native SQL Server stored procedure:
CREATE PROCEDURE dbo.Enqueue(#Payload VARCHAR(7500), #IsDeleted BIT)
WITH NATIVE_COMPILATION, SCHEMABINDING, EXECUTE AS OWNER
AS BEGIN ATOMIC WITH
(TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = 'english')
INSERT INTO dbo.SimpleQueue (Payload, IsDeleted) VALUES (#Payload, #IsDeleted);
END
GO
I'm trying to write down the Dequeue native SQL Server stored procedure, but I'm having some difficulties on how to implement an UPDATE using results of a SELECT or a variable table.
So far I tried:
CREATE PROCEDURE dbo.Dequeue(#BatchSize INT = 1)
WITH NATIVE_COMPILATION, SCHEMABINDING, EXECUTE AS OWNER
AS BEGIN ATOMIC WITH
( TRANSACTION ISOLATION LEVEL = SNAPSHOT,LANGUAGE = 'english' )
UPDATE dbo.SimpleQueue
SET IsDeleted=1
WHERE MsgId = (
SELECT TOP(#BatchSize) MsgId, Payload
FROM dbo.SimpleQueue
WHERE IsDeleted = 0)
END
GO
But I get this error:
Subqueries (queries nested inside another query) is only supported in SELECT statements with natively compiled modules.
So I tried a different approach by using a variable to store the result.
First I created a Table type:
CREATE TYPE dbo.SimpleDequeue
AS TABLE
(
MsgId BIGINT NOT NULL PRIMARY KEY NONCLUSTERED,
Payload INT NOT NULL
)
WITH (MEMORY_OPTIMIZED=ON)
GO
So far so good, then I tried to use it:
CREATE PROCEDURE dbo.Dequeue(#BatchSize INT = 1)
WITH NATIVE_COMPILATION, SCHEMABINDING, EXECUTE AS OWNER
AS BEGIN ATOMIC WITH
( TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = 'english')
DECLARE #result dbo.SimpleDequeue;
INSERT #result
SELECT TOP(#BatchSize) MsgId, Payload FROM dbo.SimpleQueue
WHERE IsDeleted = 0
UPDATE dbo.SimpleQueue
SET IsDeleted = 1
WHERE
#result.MsgId = dbo.SimpleQueue.MsgId
SELECT MsgId, Payload FROM #result
END
GO
I get this error:
Must declare the scalar variable "#result".
(only when is using #result on WHERE #result.MsgId = dbo.SimpleQueue.MsgId)
Here is the old dequeue process using in disk SQL Server tables:
CREATE PROCEDURE dbo.DequeueInDisk
#BatchSize INT = 1
AS
BEGIN
SET NOCOUNT ON;
WITH
cte AS (
SELECT TOP(#BatchSize) Payload
FROM dbo.SimpleQueue WITH (ROWLOCK, READPAST)
ORDER BY MsgId
)
DELETE FROM cte OUTPUT deleted.Payload;
END
How can I make that UPDATE and OUTPUT the updated values (with high performance, since this is critical)?
I think your approach makes perfect sense from SQL development point of view - you have to think in sets rather than in row-by-row approach. But it looks like Microsoft thinks that you require different approach for native compiled procedures, more imperative and really row-by-row (see Implementing UPDATE with FROM or Subqueries or Implementing MERGE Functionality in a Natively Compiled Stored Procedure.
So your procedure can look like this:
create or alter procedure [dbo].[Dequeue](#BatchSize int = 1)
with native_compilation, schemabinding, execute as owner
AS
BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = 'english')
declare
#result dbo.SimpleDequeue;
declare
#MsgId int,
#Payload varchar(7500),
#i int = 0;
while #i < #BatchSize
begin
select top (1)
#MsgId = s.MsgId,
#Payload = s.Payload
from dbo.SimpleQueue as s
where
s.IsDeleted = 0
order by
s.MsgId;
if ##rowcount = 0
begin
set #i = #BatchSize;
end
else
begin
update dbo.SimpleQueue set IsDeleted = 1 where MsgId = #MsgId;
insert into #result (MsgId, Payload)
select #MsgId, #Payload;
set #i += 1;
end;
end;
select MsgId, Payload from #result;
END
I've not tested how fast it will work, but I'll definitely will test it with some real numbers, cause we have a couple of these table-queues implemented and I wonder if we can get some performance boost with Hekaton.
In your old routine you use the TOP(#BatchSize) with an ORDER BY MsgId. The new approach seems not to have this ORDER BY... You'll get random result...
Your
WHERE MsgId = (
SELECT TOP(#BatchSize) MsgId, Payload
FROM dbo.SimpleQueue
WHERE IsDeleted = 0
/*added this!*/ ORDER BY MsgId )
will come back with two columns and - probably - several rows. You cannot compare this with an "=".
What you can try:
WHERE MsgId IN (
SELECT TOP(#BatchSize) MsgId
FROM dbo.SimpleQueue
WHERE IsDeleted = 0
ORDER BY MsgId)
Or you could try to use an INNER JOIN, something like this:
UPDATE dbo.SimpleQueue
SET IsDeleted=1
FROM dbo.SimpleQeueu
INNER JOIN dbo.SimpleQueue AS sq ON dbo.SimpleQeueu.MsgId=sq.MsgId
AND sq.IsDeleted=0
--this is missing the TOP-clause
What else: You could try an INNER JOIN (SELECT TOP ... ) AS InnerSimpleQueue ON .. or maybe a CROSS APPLY.
EDIT: One more approach with a CTE:
WITH myCTE AS
(
SELECT TOP(#BatchSize) MsgId
FROM dbo.SimpleQueue
WHERE IsDeleted = 0
ORDER BY MsgId
)
UPDATE dbo.SimpleQueue
SET IsDeleted=1
FROM dbo.SimpleQeueu
INNER JOIN myCTE ON myCTE.MsgId=dbo.SimpleQueue.MsgId

Update Large Number of rows in sql server

I'm trying to update a column in a table which has ~90,000 rows. Is there is any optimized way to update the table?
I have added necessary indexes.. so that no table scans/lookups are not happening. But still it takes much time to run (1hr).
My scenario:
DECLARE #ParentID NVARCHAR(100),
#Con_ERID INT
DECLARE #MaxCount INT,
#MinCount INT,
#Id INT
SELECT #MaxCount = MAX(Id) from [dbo].[ParentIDStaging] where Type='grid'
SET #MinCount = 1
WHILE #MinCount <= #MaxCount
BEGIN
SELECT #Id = ConID FROM [dbo].[ParentIDStaging] WHERE Id = #MinCount and Type = 'grid'
IF #Id IS NOT NULL
BEGIN
SELECT #Con_ERID = ErId FROM Context (NOLOCK) Where ConId = #Id
SELECT #ParentID = Identifier FROM Recording (NOLOCK) where ErId = #Con_ERID
BEGIN TRAN
UPDATE [ParentIDStaging] WITH (ROWLOCK)
SET [ParentID] = #ParentID
WHERE ContentType = 'grid'
AND ConID = #Id
COMMIT
END
SET #MinCount = #MinCount + 1
END
Looping is slow. Try doing it in one update with include the relevant other tables using joins. Your query can probably be writen like this (don't know your actual schema):
UPDATE PS
SET PS.ParentID = Recording.Identifier
FROM ParetnIDStaging PS
JOIN Context on (Context.ConId = PS.ConId)
JOIN Recording on (Recording.ErId = Context.ErId)
WHERE ...
It is because you are looping and updating one record at a time and using explicit locks/transactions.
Without knowing your underlying structure - I would bet you could do what you are trying with an update from a select.
UPDATE ParentIDStaging
SET parentIdStaging.ParentID=recording.Identifier
from ParentIDStaging
join Context on context.ConId = ParentIDStaging.ConId
join recording on contect.erid=recording.erId
WHERE parentIdStaging.ContentType = 'grid'
AND parentidStaging.Type='grid'

Resources