TSQL: Multiple IF conditions in a stored procedure - tsql

Hope I can get suggestions.
I created a stored procedure, but it is not working as intended. Basically if a value exists in a table then it will just select it, else it will insert rows to that table.
TABLE_A -- This is where I need to check if the code exists
CODE
LETTER
GREEEN
A
YELLOW
B
TABLE_B -- I use this table to get letters, there are only 2 groups.
GROUP
LETTERS
1
A
1
B
2
C
This is the stored procedure I made
CREATE PROCEDURE GET_CODE
DECLARE
#CODE VARCHAR(5) = 'RED'
,#GROUP INT = '1'
AS
BEGIN
DECLARE
#GROUPID dbo.UniqueID
,#COUNT = (SELECT count(Code) from TABLE_A where [Code] = #CODE)
/** Get letters **/
IF #GROUP = '1'
begin
insert into #GROUPID
select LETTERS from TABLE B
where GROUP = #GROUP
end
else IF #GROUP = '2'
begin
insert into #GROUPID
select LETTERS from TABLE B
where GROUP = #GROUP
end
/** if not exists insert, else go to next step **/
IF #COUNT = 0
begin
insert into TABLE_A VALUES
select
#CODE
,LETTERS
FROM #GROUPid
end
/** select where #code **/
SELECT * FROM TABLE_A where CODE = #CODE
END
Works well if the code exist, but if the code does not exist, it does not insert to TABLE_A

You're sproc is never going to insert into TABLE_A because you did not tell it to. You are inserting into the variable #GROUPID.
This will create a temporary table (note the #) and populate it with a row
DROP TABLE IF EXISTS #table_a
CREATE TABLE #table_a (code NVARCHAR(10), groupID INT)
INSERT INTO #table_a (code, groupID) VALUES
('Green', 2)
SELECT *
FROM #table_a
code groupID
---------------
Green 2
If we then do what you're attempting (outside of a stored procedure for now):
DECLARE #code NVARCHAR(10) = 'Red', #groupID INT = 1
IF NOT EXISTS (SELECT 1 FROM #table_a WHERE #code = code AND groupID = #groupID)
BEGIN
INSERT INTO #table_a (code, groupID) VALUES
(#code, #groupID)
END
SELECT *
FROM #table_a
WHERE code = #code
AND groupID = #groupID
code groupID
---------------
Red 1
First we declare some variables, and give them values.
Then we look to see if the values exist in the table using NOT EXISTS.
If that returns true we INSERT those values into #table_a.
Since we now know that they will exist (they either did already, or we just inserted them) we can then just select those values.
If we do the same thing again, but this time with existing values we can see it doesn't insert them again:
SET #code = 'Red'
SET #groupID = 1
IF NOT EXISTS (SELECT 1 FROM #table_a WHERE #code = code AND groupID = #groupID)
BEGIN
INSERT INTO #table_a (code, groupID) VALUES
(#code, #groupID)
END
SELECT *
FROM #table_a
WHERE code = #code
AND groupID = #groupID
code groupID
Red 1

Related

Increment read count giving incorrect output

Hi I have stored procedure with Merge statements using the cursor to count Read, Insert, update and delete with an increment read count
CREATE PROCEDURE StagingProcedure
AS
BEGIN
DECLARE #output TABLE(change VARCHAR(20));
DECLARE #Reads INT,
#Inserts INT =0,
#Updates INT=0,
#Deletes INT=0;
DECLARE #Cursor_Month NVARCHAR(10)
DECLARE CUR_MONTHS CURSOR FAST_FORWARD FOR
SELECT DISTINCT year_month_submission
FROM StagingFile
WHERE Type = 'Payment' AND [status] IN('New','Updated')
ORDER BY year_month_submission ASC
OPEN CUR_MONTHS
FETCH NEXT FROM CUR_MONTHS INTO #Cursor_Month
WHILE ##FETCH_STATUS = 0
BEGIN
--INSERT THE MAIN MERGE CODE HERE, this example
**SELECT #Reads = #reads + COUNT(*)**
FROM StagingFile bf
INNER JOIN StagingData bd
ON bf.FileID = bd.FileID
WHERE bf.Type = 'Claim' and bf.[status] IN('New','Updated') and bf.year_month_submission = #Cursor_Month
DECLARE #FilesList TABLE(fileuid UNIQUEIDENTIFIER)
INSERT INTO #FilesList (fileid) (SELECT DISTINCT FileID FROM StagingFile
WHERE [status] IN('New','Updated') and Type = 'Payment' and year_month_submission = #Cursor_BDXMonth)
MERGE INTO Table a WITH (HOLD LOCK) AS tgt
USING (
SELECT Distinct
JSON_VALUE(DocumentJSON, '$.EntityID') AS EntityID,
CASE JSON_VALUE(DocumentJSON, '$.Denial')
WHEN 'Yes' THEN 'Y'
WHEN 'No' THEN 'N'
ELSE JSON_VALUE(DocumentJSON, '$.Denial')
END AS Denial
FROM Table1 bd
INNER JOIN table2 bf ON bf.FileID = bd.FileID
WHERE bf.Type = 'Payment'
) AS src ON tgt.[ID] = src.[ID]
WHEN MATCHED
)) THEN
UPDATE SET tgt.ID = src.ID,
tgt.EntityID = src.EntityID,
tgt.Denial = src.Denial,
WHEN NOT MATCHED BY THE TARGET
THEN INSERT (ID, EntityID, Denial)
VALUES (src. ID, src.EntityID, src. Denial)
WHEN NOT MATCHED BY THE SOURCE
AND tgt.FileID IN(SELECT fileid FROM #FilesList)
THEN update
Set tgt.Isdeleted=1,
tgt.lastupdated=getdate()
THEN DELETE
OUTPUT $action INTO #output;
SELECT #Inserts = #Inserts + count(*) FROM #output WHERE change = 'INSERT'
SELECT #Updates = #Updates + count(*) FROM #output WHERE change = 'Update'
SELECT #Deletes = #Deletes + count(*) FROM #output WHERE change ='Delete'
FETCH NEXT FROM CUR_MONTHS INTO #Cursor_BDXMonth
END
SELECT #Reads ReadCount, #Inserts InsertCount, #Updates UpdateCount, #Deletes DeleteCount;
CLOSE CUR_MONTHS
DEALLOCATE CUR_MONTHS
END;
The output of StoredProcedure not syncing with the insert statement, Please could advise what this script exactly doing is there an issue in the Cursor statement under Read count not sure if this is something to incremental read count. I don't have 45 updated records seems like somewhere it's counting wrongly.
ReadCount
InsertCount
UpdateCount
DeleteCount
38
21
45
13
---------
-----------
-----------
-----------

Run a stored procedure using select columns as input parameters?

I have a select query that returns a dataset with "n" records in one column. I would like to use this column as the parameter in a stored procedure. Below a reduced example of my case.
The query:
SELECT code FROM rawproducts
The dataset:
CODE
1
2
3
The stored procedure:
ALTER PROCEDURE [dbo].[MyInsertSP]
(#code INT)
AS
BEGIN
INSERT INTO PRODUCTS description, price, stock
SELECT description, price, stock
FROM INVENTORY I
WHERE I.icode = #code
END
I already have the actual query and stored procedure done; I just am not sure how to put them both together.
I would appreciate any assistance here! Thank you!
PS: of course the stored procedure is not as simple as above. I just choose to use a very silly example to keep things small here. :)
Here's two methods for you, one using a loop without a cursor:
DECLARE #code_list TABLE (code INT);
INSERT INTO #code_list SELECT code, ROW_NUMBER() OVER (ORDER BY code) AS row_id FROM rawproducts;
DECLARE #count INT;
SELECT #count = COUNT(*) FROM #code_list;
WHILE #count > 0
BEGIN
DECLARE #code INT;
SELECT #code = code FROM #code_list WHERE row_id = #count;
EXEC MyInsertSP #code;
DELETE FROM #code_list WHERE row_id = #count;
SELECT #count = COUNT(*) FROM #code_list;
END;
This works by putting the codes into a table variable, and assigning a number from 1..n to each row. Then we loop through them, one at a time, deleting them as they are processed, until there is nothing left in the table variable.
But here's what I would consider a better method:
CREATE TYPE dbo.code_list AS TABLE (code INT);
GO
CREATE PROCEDURE MyInsertSP (
#code_list dbo.code_list)
AS
BEGIN
INSERT INTO PRODUCTS (
[description],
price,
stock)
SELECT
i.[description],
i.price,
i.stock
FROM
INVENTORY i
INNER JOIN #code_list cl ON cl.code = i.code;
END;
GO
DECLARE #code_list dbo.code_list;
INSERT INTO #code_list SELECT code FROM rawproducts;
EXEC MyInsertSP #code_list = #code_list;
To get this to work I create a user-defined table type, then use this to pass a list of codes into the stored procedure. It means slightly rewriting your stored procedure, but the actual code to do the work is much smaller.
(how to) Run a stored procedure using select columns as input
parameters?
What you are looking for is APPLY; APPLY is how you use columns as input parameters. The only thing unclear is how/where the input column is populated. Let's start with sample data:
IF OBJECT_ID('dbo.Products', 'U') IS NOT NULL DROP TABLE dbo.Products;
IF OBJECT_ID('dbo.Inventory','U') IS NOT NULL DROP TABLE dbo.Inventory;
IF OBJECT_ID('dbo.Code','U') IS NOT NULL DROP TABLE dbo.Code;
CREATE TABLE dbo.Products
(
[description] VARCHAR(1000) NULL,
price DECIMAL(10,2) NOT NULL,
stock INT NOT NULL
);
CREATE TABLE dbo.Inventory
(
icode INT NOT NULL,
[description] VARCHAR(1000) NULL,
price DECIMAL(10,2) NOT NULL,
stock INT NOT NULL
);
CREATE TABLE dbo.Code(icode INT NOT NULL);
INSERT dbo.Inventory
VALUES (10,'',20.10,3),(11,'',40.10,3),(11,'',25.23,3),(11,'',55.23,3),(12,'',50.23,3),
(15,'',33.10,3),(15,'',19.16,5),(18,'',75.00,3),(21,'',88.00,3),(21,'',100.99,3);
CREATE CLUSTERED INDEX uq_inventory ON dbo.Inventory(icode);
The function:
CREATE FUNCTION dbo.fnInventory(#code INT)
RETURNS TABLE AS RETURN
SELECT i.[description], i.price, i.stock
FROM dbo.Inventory I
WHERE I.icode = #code;
USE:
DECLARE #code TABLE (icode INT);
INSERT #code VALUES (10),(11);
SELECT f.[description], f.price, f.stock
FROM #code AS c
CROSS APPLY dbo.fnInventory(c.icode) AS f;
Results:
description price stock
-------------- -------- -----------
20.10 3
40.10 3
Updated Proc (note my comments):
ALTER PROC dbo.MyInsertSP -- (1) Lose the input param
AS
-- (2) Code that populates the "code" table
INSERT dbo.Code VALUES (10),(11);
-- (3) Use CROSS APPLY to pass the values from dbo.code to your function
INSERT dbo.Products ([description], price, stock)
SELECT f.[description], f.price, f.stock
FROM dbo.code AS c
CROSS APPLY dbo.fnInventory(c.icode) AS f;
This ^^^ is how it's done.

DELETE WHERE NOT EXISTS Not Being Applied Correctly

I'm using NOT EXISTS during a DELETE statement in a stored procedure and the not exists is not being applied to the data.
Using the following example data:
CREATE TABLE Region
(
RegionID INT IDENTITY(1,1)
,RegionName VARCHAR(25)
)
GO
INSERT INTO Region(RegionName)
VALUES ('East Coast')
,('Mid West')
,('West Coast')
GO
CREATE TABLE Customer
(
CustomerID INT IDENTITY(1,1)
,FirstName VARCHAR(5)
,Region INT
)
GO
INSERT INTO Customer(FirstName,Region)
VALUES('Tom',1)
,('Mike',2)
,('Jean',3)
GO
CREATE TABLE Orders
(
OrderID INT IDENTITY(1,1)
,CustomerID INT
,OrderAmount INT
,OrderDate DATE
)
GO
INSERT INTO Orders(CustomerID,OrderAmount,OrderDate)
VALUES(1,10,'2018-11-30')
,(2,12,'2018-11-30')
,(2,15,'2018-12-01')
,(2,8,'2018-12-02')
,(2,11,'2018-12-03')
,(3,13,'2018-12-01')
,(3,20,'2018-12-03')
GO
Using that data I'm trying to create a procedure that does the following:
CREATE PROCEDURE udsp_GetOrdersOfXAmount #OrderAmount INT, #RegionID INT = 0
AS
BEGIN
DECLARE #ProcedureTemp TABLE
(
OrderID INT
,CustomerID INT
,OrderAmount INT
,OrderDate DATE
)
INSERT INTO #ProcedureTemp(OrderID,CustomerID,OrderAmount,OrderDate)
SELECT * FROM Orders WHERE OrderAmount >= #OrderAmount
--Do several other UPDATES/ DELETES to #ProcedureTemp
--This is where the issue lies
IF #RegionID > 0
BEGIN
DELETE T FROM #ProcedureTemp T
WHERE NOT EXISTS
(
SELECT *
FROM Customer C
JOIN #ProcedureTemp T ON T.CustomerID = C.CustomerID
WHERE C.Region = #RegionID
)
END
SELECT * FROM #ProcedureTemp
END
GO
If you execute the procedure with the #RegionID parameter populated, you will see the procedure is not honoring the filter by region.
E.G.
EXEC udsp_GetOrdersOfXAmount 10,3
However, if you run the sub query used in the DELETE statement as its own query, you will see the WHERE clause logic provided is working. I don't understand why the it isn't working when used with NOT EXISTS in the DELETE statement.
DECLARE #OrderAmount INT = 10, #RegionID INT = 3
DECLARE #ProcedureTemp TABLE
(
OrderID INT
,CustomerID INT
,OrderAmount INT
,OrderDate DATE
)
INSERT INTO #ProcedureTemp(OrderID,CustomerID,OrderAmount,OrderDate)
SELECT * FROM Orders WHERE OrderAmount >= #OrderAmount
SELECT *
FROM Customer C
JOIN #ProcedureTemp T ON T.CustomerID = C.CustomerID
WHERE C.Region = #RegionID
Thank you in advance for any help you can provide.
You don't need the join in the inner query.
The fact that you are using the same alias for the outer query and the inner one is confusing to me, I'm guessing SQL Server also should have a problem with it.
Try writing it like this:
DELETE T
FROM #ProcedureTemp T
WHERE NOT EXISTS
(
SELECT *
FROM Customer C
-- You already have the T from the outer statement
WHERE T.CustomerID = C.CustomerID
AND C.Region = #RegionID
)

PostgreSql: how to update table from array?

How to update table fields from array to avoid temp table using?
User passes array to reorder table.
create TABLE items
(
id serial primary key,
name text,
select_order int
)
insert into items(name, select_order)
values
('cucumber',0),
('milk',1),
('coffee',2),
('water',3)
select * from items order by select_order;
DO
$body$
DECLARE var_array int[]:='{3,0,2,1}';
BEGIN
update items ??
END;
$body$
The final result in this example should be
select * from items order by select_order;
name select_order
------------------------
water 0
cucumber 1
coffee 2
milk 3
Assuming the index in the array corresponds to the value in select_order the following query returns the new and old value:
select i.id, i.select_order, t.new_sort
from items i
join lateral unnest(array[3,0,2,1]) with ordinality as t(new_sort, idx) on t.idx - 1 = i.select_order
Now this can be used to update the target table:
update items
set select_order = r.new_sort
from (
select i.id, i.select_order, t.new_sort
from items i
join lateral unnest(array[3,0,2,1]) with ordinality as t(new_sort, idx) on t.idx - 1 = i.select_order
) r
where r.id = items.id;
This also assumes that select_order starts a 0 and has no gaps.

How to filter records for all rows?

I am designing a SQL query to extract all records from a given table. But the trick here is that this logic is based on a numeric database field. So there are 4 choices: 0,1,2,3. If user selects 0,1, or 2, then my query returns rows with the specified value. But if they choose 3, it should return all of the rows. How do I do this in SQL? I know if this was a string, I could do something like:
WHERE = CASE WHEN = 3 THEN '%' ELSE END
But in this case, is an integer. Sounds relatively simple but I'm getting errors.
Try this:
SELECT *
FROM <YOUR_TABLE>
WHERE
(
<YOUR_COLUMN> = #InputValue OR
3 = #InputValue
)
Where #InputValue is the name of parameter sent to the query.
The simplest way is to do this:
select MyColumn
from MyTable
where ( MyValue = #MyParameter or #MyParameter = 3)
If your interested in better optimization, then you can do this, but it is less maintainable:
if (#MyParameter = 3)
select MyColumn
from MyTable
else
select MyColumn
from MyTable
where MyValue = #MyParameter
If I were forced to implement this functionality, then I would probably do this, just to make things clear:
declare #AllRecords nchar(1)
if (#MyParameter = 3)
set #AllRecords = N'Y'
else
set #AllRecords = N'N'
select MyColumn
from MyTable
where (MyValue = #MyParameter or #AllRecords = N'Y')
Hopefully, I won't ever have to implement a system that mixes flags and data value in this way.
UPDATED
Here is a version that should work with your expanded requirements (this requires one of the newer versions of SQL Server, I think):
declare #SelectedLevels table (LevelId int not null primary key)
if #LevelId = 3
insert into #SelectedLevels (LevelId) values (1), (2)
else if #LevelId = 5
insert into #SelectedLevels (LevelId) values (0), (1), (2)
else
insert into #SelectedLevels (LevelId) values (#LevelId)
select mt.MyColumn
from MyTable mt
inner join #SelectedLevels sl on sl.LevelId = MyTable.LevelId
if #Param = 3
begin
select *
from #T
end
else
if #Param = 2
begin
select *
from #T
where id in (0,1)
end
else
begin
select *
from #T
where id = #Param
end