patternsqlMinor
SQL Stored Procedure Get Distinct and Update
Viewed 0 times
storeddistinctupdatesqlproceduregetand
Problem
The idea is to show how many times a user (by
I have been learning Fetch this week so wanted to ensure I have gone down the right path and this is a small database, can/should I use this for any size?
Code:
```
USE [database]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
Alter PROCEDURE [dbo].[spSummary]
-- Add the parameters for the stored procedure here
AS
BEGIN
DECLARE @StatusTotal AS Int
DECLARE @Status0 AS Int
DECLARE @Status1 AS Int
DECLARE @Status2 AS Int
DECLARE @Status3 AS Int
--Cursor to loop through
DECLARE @EmployeeID AS NvarChar(10);
DECLARE propCurs CURSOR FOR
SELECT EmployeeID FROM [TblTableList]
--Open Loop Code
OPEN propCurs;
FETCH NEXT FROM propCurs INTO @EmployeeID
WHILE @@FETCH_STATUS = 0
--Now do repeated code
BEGIN
-- SELECT @StatusTotal = COUNT(ID) FROM [TblTableCountFrom] WHERE EmployeeID = @EmployeeID
--SELECT @Status0 = COUNT(DISTINCT(ID)) FROM [TblTableCountFrom] WHERE EmployeeID = @EmployeeID AND CurrentAlertStatus = '0'
--SELECT @Status1 = COUNT(DISTINCT(ID)) FROM [TblTableCountFrom] WHERE EmployeeID = @EmployeeID AND CurrentAlertStatus = '1'
--SELECT @Status2 = COUNT(DISTINCT(ID)) FROM [TblTableCountFrom] WHERE EmployeeID = @EmployeeID AND CurrentAlertStatus = '2'
--SELECT @Status3 = COUNT(DISTINCT(ID)) FROM [TblTableCountFrom] WHERE EmployeeID = @EmployeeID AND CurrentAlertStatus = '3'
with cnt as (
SELECT
1 as cntAll
, case WHEN CurrentAlertStatus = '0' then 1 else 0 end as cnt1
, case WHEN CurrentAlertStatus = '1' then 1 else 0 end as cnt2
, case WHEN CurrentAlertStatus = '2' then 1 else 0 end as c
EmployeeID) is in the TblTableList and then update the TblTableStatusCountI have been learning Fetch this week so wanted to ensure I have gone down the right path and this is a small database, can/should I use this for any size?
Code:
```
USE [database]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
Alter PROCEDURE [dbo].[spSummary]
-- Add the parameters for the stored procedure here
AS
BEGIN
DECLARE @StatusTotal AS Int
DECLARE @Status0 AS Int
DECLARE @Status1 AS Int
DECLARE @Status2 AS Int
DECLARE @Status3 AS Int
--Cursor to loop through
DECLARE @EmployeeID AS NvarChar(10);
DECLARE propCurs CURSOR FOR
SELECT EmployeeID FROM [TblTableList]
--Open Loop Code
OPEN propCurs;
FETCH NEXT FROM propCurs INTO @EmployeeID
WHILE @@FETCH_STATUS = 0
--Now do repeated code
BEGIN
-- SELECT @StatusTotal = COUNT(ID) FROM [TblTableCountFrom] WHERE EmployeeID = @EmployeeID
--SELECT @Status0 = COUNT(DISTINCT(ID)) FROM [TblTableCountFrom] WHERE EmployeeID = @EmployeeID AND CurrentAlertStatus = '0'
--SELECT @Status1 = COUNT(DISTINCT(ID)) FROM [TblTableCountFrom] WHERE EmployeeID = @EmployeeID AND CurrentAlertStatus = '1'
--SELECT @Status2 = COUNT(DISTINCT(ID)) FROM [TblTableCountFrom] WHERE EmployeeID = @EmployeeID AND CurrentAlertStatus = '2'
--SELECT @Status3 = COUNT(DISTINCT(ID)) FROM [TblTableCountFrom] WHERE EmployeeID = @EmployeeID AND CurrentAlertStatus = '3'
with cnt as (
SELECT
1 as cntAll
, case WHEN CurrentAlertStatus = '0' then 1 else 0 end as cnt1
, case WHEN CurrentAlertStatus = '1' then 1 else 0 end as cnt2
, case WHEN CurrentAlertStatus = '2' then 1 else 0 end as c
Solution
Consistency
You have this:
And this:
While both styles of formatting are perfectly acceptable, it's better to sick with the same throughout your code for readability.
Same for this:
And this:
It works with or without a semicolon in this case, try to stick to one method.
The elephant in the room
Cursors are very slow. Especially using a bunch of variables with it. I understand you were experimenting with a function of SQL you are new to, and that's great. We all do that, play with our shiny new toy.
However, for something that's production and that will get called regularly, efficiency is key. Your clients won't care how cool your code looks, especially if it takes longer to run.
It is very rare that you actually need a cursor. Think about it this way. If you have 100 employees in your table, then it is running the query 100 times instead of once. Imagine 5000 or 50000 employees how long this would take.
I commented out all sections I think you could safely dispense with for better efficiency:
Also!
Do what @ckuhn203 said about a view.
You have this:
with cnt as (
SELECT
1 as cntAll
, case WHEN CurrentAlertStatus = '0' then 1 else 0 end as cnt1
, case WHEN CurrentAlertStatus = '1' then 1 else 0 end as cnt2
, case WHEN CurrentAlertStatus = '2' then 1 else 0 end as cnt3
, case WHEN CurrentAlertStatus = '3' then 1 else 0 end as cnt4
from [TblTableCountFrom] where EmployeeID = @EmployeeIDAnd this:
SELECT @StatusTotal = sum(cntAll),
@Status0 = sum(cnt1),
@Status1 = sum(cnt2),
@Status2 = sum(cnt3),
@Status3 = sum(cnt4)
FROM cnt;While both styles of formatting are perfectly acceptable, it's better to sick with the same throughout your code for readability.
Same for this:
DECLARE @StatusTotal AS IntAnd this:
DECLARE @EmployeeID AS NvarChar(10);It works with or without a semicolon in this case, try to stick to one method.
The elephant in the room
Cursors are very slow. Especially using a bunch of variables with it. I understand you were experimenting with a function of SQL you are new to, and that's great. We all do that, play with our shiny new toy.
However, for something that's production and that will get called regularly, efficiency is key. Your clients won't care how cool your code looks, especially if it takes longer to run.
It is very rare that you actually need a cursor. Think about it this way. If you have 100 employees in your table, then it is running the query 100 times instead of once. Imagine 5000 or 50000 employees how long this would take.
I commented out all sections I think you could safely dispense with for better efficiency:
USE [database]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
Alter PROCEDURE [dbo].[spSummary]
-- Add the parameters for the stored procedure here
AS
BEGIN
DECLARE @StatusTotal AS Int
DECLARE @Status0 AS Int
DECLARE @Status1 AS Int
DECLARE @Status2 AS Int
DECLARE @Status3 AS Int
--Cursor to loop through
DECLARE @EmployeeID AS NvarChar(10);
/* DECLARE propCurs CURSOR FOR
SELECT EmployeeID FROM [TblTableList]
--Open Loop Code
OPEN propCurs;
FETCH NEXT FROM propCurs INTO @EmployeeID
WHILE @@FETCH_STATUS = 0
--Now do repeated code
BEGIN */
-- SELECT @StatusTotal = COUNT(ID) FROM [TblTableCountFrom] WHERE EmployeeID = @EmployeeID
-- SELECT @Status0 = COUNT(DISTINCT(ID)) FROM [TblTableCountFrom] WHERE EmployeeID = @EmployeeID AND CurrentAlertStatus = '0'
-- SELECT @Status1 = COUNT(DISTINCT(ID)) FROM [TblTableCountFrom] WHERE EmployeeID = @EmployeeID AND CurrentAlertStatus = '1'
-- SELECT @Status2 = COUNT(DISTINCT(ID)) FROM [TblTableCountFrom] WHERE EmployeeID = @EmployeeID AND CurrentAlertStatus = '2'
-- SELECT @Status3 = COUNT(DISTINCT(ID)) FROM [TblTableCountFrom] WHERE EmployeeID = @EmployeeID AND CurrentAlertStatus = '3'
with cnt as (
SELECT
1 as cntAll
, case WHEN CurrentAlertStatus = '0' then 1 else 0 end as cnt1
, case WHEN CurrentAlertStatus = '1' then 1 else 0 end as cnt2
, case WHEN CurrentAlertStatus = '2' then 1 else 0 end as cnt3
, case WHEN CurrentAlertStatus = '3' then 1 else 0 end as cnt4
from [TblTableCountFrom] where EmployeeID = @EmployeeID
)
SELECT @StatusTotal = sum(cntAll),
@Status0 = sum(cnt1),
@Status1 = sum(cnt2),
@Status2 = sum(cnt3),
@Status3 = sum(cnt4)
FROM cnt;
-- Check if ID Exists
--HERE NEED TO CHECK IF EXISTS OR SHOULD I WIPE THE TABLE AT THE START?
IF EXISTS (SELECT EmployeeID FROM [TblTableCount] WHERE @EmployeeID = EmployeeID)
UPDATE [TblTableCountFrom]Count
SET StatusTotalCount = @StatusTotal, Status0 = @Status0, Status1 = @Status1, Status2 = @Status2, Status3 = @Status3
WHERE EmployeeID = @EmployeeID
Else
INSERT INTO [TblTableCount]
(EmployeeID, StatusTotalCount, Status0, Status1 , Status2, Status3) VALUES (@EmployeeID, @StatusTotal,@Status0, @Status1, @Status2,@Status3 )
/* END
-- Now we bookend the loop code
FETCH NEXT FROM propCurs INTO @EmployeeID */
END
--Tidy up and close loop code
/* CLOSE propCurs;
DEALLOCATE propCurs; */Also!
Do what @ckuhn203 said about a view.
Code Snippets
with cnt as (
SELECT
1 as cntAll
, case WHEN CurrentAlertStatus = '0' then 1 else 0 end as cnt1
, case WHEN CurrentAlertStatus = '1' then 1 else 0 end as cnt2
, case WHEN CurrentAlertStatus = '2' then 1 else 0 end as cnt3
, case WHEN CurrentAlertStatus = '3' then 1 else 0 end as cnt4
from [TblTableCountFrom] where EmployeeID = @EmployeeIDSELECT @StatusTotal = sum(cntAll),
@Status0 = sum(cnt1),
@Status1 = sum(cnt2),
@Status2 = sum(cnt3),
@Status3 = sum(cnt4)
FROM cnt;DECLARE @StatusTotal AS IntDECLARE @EmployeeID AS NvarChar(10);USE [database]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
Alter PROCEDURE [dbo].[spSummary]
-- Add the parameters for the stored procedure here
AS
BEGIN
DECLARE @StatusTotal AS Int
DECLARE @Status0 AS Int
DECLARE @Status1 AS Int
DECLARE @Status2 AS Int
DECLARE @Status3 AS Int
--Cursor to loop through
DECLARE @EmployeeID AS NvarChar(10);
/* DECLARE propCurs CURSOR FOR
SELECT EmployeeID FROM [TblTableList]
--Open Loop Code
OPEN propCurs;
FETCH NEXT FROM propCurs INTO @EmployeeID
WHILE @@FETCH_STATUS = 0
--Now do repeated code
BEGIN */
-- SELECT @StatusTotal = COUNT(ID) FROM [TblTableCountFrom] WHERE EmployeeID = @EmployeeID
-- SELECT @Status0 = COUNT(DISTINCT(ID)) FROM [TblTableCountFrom] WHERE EmployeeID = @EmployeeID AND CurrentAlertStatus = '0'
-- SELECT @Status1 = COUNT(DISTINCT(ID)) FROM [TblTableCountFrom] WHERE EmployeeID = @EmployeeID AND CurrentAlertStatus = '1'
-- SELECT @Status2 = COUNT(DISTINCT(ID)) FROM [TblTableCountFrom] WHERE EmployeeID = @EmployeeID AND CurrentAlertStatus = '2'
-- SELECT @Status3 = COUNT(DISTINCT(ID)) FROM [TblTableCountFrom] WHERE EmployeeID = @EmployeeID AND CurrentAlertStatus = '3'
with cnt as (
SELECT
1 as cntAll
, case WHEN CurrentAlertStatus = '0' then 1 else 0 end as cnt1
, case WHEN CurrentAlertStatus = '1' then 1 else 0 end as cnt2
, case WHEN CurrentAlertStatus = '2' then 1 else 0 end as cnt3
, case WHEN CurrentAlertStatus = '3' then 1 else 0 end as cnt4
from [TblTableCountFrom] where EmployeeID = @EmployeeID
)
SELECT @StatusTotal = sum(cntAll),
@Status0 = sum(cnt1),
@Status1 = sum(cnt2),
@Status2 = sum(cnt3),
@Status3 = sum(cnt4)
FROM cnt;
-- Check if ID Exists
--HERE NEED TO CHECK IF EXISTS OR SHOULD I WIPE THE TABLE AT THE START?
IF EXISTS (SELECT EmployeeID FROM [TblTableCount] WHERE @EmployeeID = EmployeeID)
UPDATE [TblTableCountFrom]Count
SET StatusTotalCount = @StatusTotal, Status0 = @Status0, Status1 = @Status1, Status2 = @Status2, Status3 = @Status3
WHERE EmployeeID = @EmployeeID
Else
INSERT INTO [TblTableCount]
(EmployeeID, StatusTotalCount, Status0, Status1 , Status2, Status3) VALUES (@EmployeeID, @StatusTotal,@Status0, @Status1, @Status2,@Status3 )
/* END
-- Now we bookend the loop code
FETCH NEXT FROM propCurs INTO @EmployeeID */
END
--Tidy up and close loop code
/* CLOSE propCurs;
DEALLOCATE propCurs; */Context
StackExchange Code Review Q#57284, answer score: 6
Revisions (0)
No revisions yet.