Using IF without an ELSE condition in SQL - sql

I'm trying to use the below query to update some data only if #rowcount is greater than 0. However, its executing the update statement even when #RowCount is 0. Can someone help whats wrong in here please? I would like to do nothing if #RowCount is 0.
I'm using SQL Server 2014.
TRUNCATE TABLE Count1
DECLARE #RowCount AS INT
--insert data in a temporary table
SELECT YEAR, S_ID
into #Count1 FROM
(SELECT DISTINCT D.YEAR, S_ID FROM SALES S JOIN TRANSACTIONS PT
ON S.COMBINED_TXN_ID = PT.S_ID AND PT.TRANSACTION_TYPE = 'BILLING'
JOIN DATE D ON D.DAY = S.DAY AND PT.DAY = S.DAY
WHERE
S.SALES_CUSTOMER != PT.CUSTOMER)Counter1;
--Store the rowcount in a temporary variable
SET #RowCount = (SELECT Count(*) FROM #Count1)
--Fix the data with differences only if count>0
IF ##ROWCOUNT > 0
BEGIN
UPDATE SALES
SET SALES_CUSTOMER = PT.CUSTOMER
FROM SALES S
JOIN TRANSACTIONS PT ON S.COMBINED_TXN_ID = PT.S_ID
JOIN DATE D ON D.DAY = S.DAY AND PT.DAY = S.DAY
WHERE
S_ID IN (SELECT S_ID FROM #Count1)
END;

##ROWCOUNT Returns the number of rows affected by the last statement.
Change system variable ##ROWCOUNT by your own variable #RowCount
--Store the rowcount in a temporary variable
SET #RowCount = (SELECT Count(*) FROM #Count1)
--Fix the data with differences only if count>0
IF #RowCount > 0

You are confusing the local variable #RowCount you defined yourself, with the global variable ##ROWCOUNT Microsoft defined for you (which you can't set).
So simply refer to the correct variable. Better yet: rename your local variable to avoid such confusion.
You don't even need the if at all, since the update wouldn't be executed if #Count1 is empty as there are no records to fulfill the where clause (WHERE S_ID IN ({empty result set})).

If you wish to use the same variable you have declared use #rowcount
DECLARE #recordCount INT
--insert data in a temporary table
SELECT YEAR
,S_ID
INTO #Count1
FROM (
SELECT DISTINCT D.YEAR
,S_ID
FROM SALES S
INNER JOIN TRANSACTIONS PT
ON S.COMBINED_TXN_ID = PT.S_ID
AND PT.TRANSACTION_TYPE = 'BILLING'
INNER JOIN DATE D
ON D.DAY = S.DAY
AND PT.DAY = S.DAY
WHERE S.SALES_CUSTOMER != PT.CUSTOMER
) Counter1;
--Store the rowcount in a temporary variable
SELECT #recordCount = count(*)
FROM #Count1
--Fix the data with differences only if count>0
IF (#recordCount > 0)
BEGIN
UPDATE SALES
SET SALES_CUSTOMER = PT.CUSTOMER
FROM SALES S
INNER JOIN TRANSACTIONS PT
ON S.COMBINED_TXN_ID = PT.S_ID
INNER JOIN DATE D
ON D.DAY = S.DAY
AND PT.DAY = S.DAY
WHERE S_ID IN (
SELECT S_ID
FROM #Count1
)
END

You could skip all the temp tables and checks and just do an update. Something like this should be the same thing as all the code you posted.
UPDATE S
SET SALES_CUSTOMER = PT.CUSTOMER
FROM SALES S
JOIN S PT ON S.COMBINED_TXN_ID = PT.S_ID
AND PT.TRANSACTION_TYPE = 'BILLING'
JOIN DATE D ON D.DAY = S.DAY
AND PT.DAY = S.DAY
WHERE S.SALES_CUSTOMER != PT.CUSTOMER

Related

Used Cursor inside cursor for update it is showing o rows effected, logic is working when i tried manually

stored procedure
Used Cursor inside cursor for update it is showing 0 rows effected, logic is working when i tried manually, declaring and closing done properly.
any changes do i need to do
or any alternatives than cursor.
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
ALTER PROCEDURE [dbo].[POS_Discount_Report]
#OutletId INT = NULL,
#FromDate DATE = NULL,
#ToDate DATE = NULL,
#DiscountPercent DECIMAL = NULL
AS
begin
SELECT #CutOffInvoiceAmount = AVG(InvoiceAmount) FROM POS_SalesReceiptMaster WHERE StampDate BETWEEN #FromDate AND #ToDate
DECLARE Receipt_cursor CURSOR FOR
SELECT Id FROM POS_SalesReceiptMaster WHERE StampDate BETWEEN #FromDate AND #ToDate AND InvoiceAmount <= #CutOffInvoiceAmount
OPEN Receipt_cursor
FETCH NEXT FROM Receipt_cursor
INTO #ReceiptId
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE Item_cursor CURSOR FOR
SELECT Id FROM Updated_SalesReceiptItems WHERE ReceiptId = #ReceiptId
OPEN Item_cursor
FETCH NEXT FROM Item_cursor
INTO #ID
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #Percentage = Percentage, #ItemPrice = Price FROM
Updated_SalesReceiptItems WHERE Id = #ID
IF #Percentage = 5
BEGIN
SELECT #UpdatePercentage = Tax5 FROM Updated_Master
Where Percentage = #DiscountPercent
END
ELSE
BEGIN
#UpdatePercentage = 5
END
UPDATE Updated_SalesReceiptItems
SET ProductId = Product.ProductId,
Actualprice = Product.Actualprice,
Quantity = Product.Qty,
ProductName = Product.ProductName,
unit = Product.unit,
CategoryName= Product.CategoryName,
Percentage= Product.Percentage,
Amount = Product.Amount FROM
(SELECT TOP 1 PM.ProductId, ProductCode,
dbo.fn_Get_ProductPrice_By_Outlet(ProductId,#OutletId)
AS
Actualprice,
(CASE WHEN ( dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, ProductId) != 0)
THEN (#ItemPrice / dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, ProductId))
ELSE 0
END) AS Qty,
ProductName, Unit, CategoryName, #UpdatePercentage AS Percentage,
dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, ProductId) * (#UpdatePercentage/100) AS TaxAmount
FROM dbo.Products_Master PM
INNER JOIN ProductCategory_Master CM ON PM.CategoryId = CM.CategoryId
INNER JOIN tax_master TM ON PM.TaxId = TM.Id
WHERE (#ItemPrice) % nullif(dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, ProductId),0) = 0
AND Percentage = #UpdatePercentage) Product
WHERE Id = #ID
end
FETCH NEXT FROM Item_cursor
INTO #ID
END
CLOSE Item_cursor;
DEALLOCATE Item_cursor;
FETCH NEXT FROM Receipt_cursor
INTO #ReceiptId
END
CLOSE Receipt_cursor;
DEALLOCATE Receipt_cursor;
END
Okay, this is pretty scrappy, and probably won't work without some fixing, but it should give you the general pattern for doing all of this in a single query?
ALTER PROCEDURE POS_Discount_Report (
#OutletId INT = NULL,
#FromDate DATE = NULL,
#ToDate DATE = NULL,
#DiscountPercent DECIMAL = NULL)
AS
BEGIN
DECLARE #CutOffInvoiceAmount NUMERIC(19,2); --?? seems to be missing from original procedure
SELECT #CutOffInvoiceAmount = AVG(InvoiceAmount) FROM POS_SalesReceiptMaster WHERE StampDate BETWEEN #FromDate AND #ToDate; --What happens if one or both of these is NULL?
--CTEs
WITH Receipt AS (
SELECT Id FROM POS_SalesReceiptMaster WHERE StampDate BETWEEN #FromDate AND #ToDate AND InvoiceAmount <= #CutOffInvoiceAmount),
Item AS (
SELECT Id FROM Updated_SalesReceiptItems s INNER JOIN Receipt r ON s.ReceiptId = r.Id),
PercentQuery AS (
SELECT i.Id, u.[Percentage], u.Price FROM Updated_SalesReceiptItems u INNER JOIN Item i ON u.Id = i.Id),
UpdatePercent AS (
SELECT p.Id, p.[Percentage], p.Price, CASE WHEN p.[Percentage] = 5 THEN u.Tax5 ELSE 5 END AS UpdatePercentage FROM PercentQuery p INNER JOIN Updated_Master u ON u.[Percentage] = #DiscountPercent)
UPDATE
u
SET
ProductId = pm.ProductId,
Actualprice = dbo.fn_Get_ProductPrice_By_Outlet(ProductId, #OutletId),
Quantity =
CASE
WHEN (dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, ProductId) != 0)
THEN (#ItemPrice / dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, ProductId))
ELSE 0
END,
ProductName = pm.ProductName,
unit = pm.unit, --not sure on the alias here, as it's missing in the original query
CategoryName = pm.CategoryName,
[Percentage] = u.UpdatePercentage,
Amount = dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, ProductId) * (u.UpdatePercentage / 100) --although this was TaxAmount originally??!
FROM
dbo.Products_Master pm
INNER JOIN ProductCategory_Master cm ON cm.CategoryId = pm.CategoryId
INNER JOIN tax_master tm ON tm.Id = pm.TaxId
INNER JOIN UpdatePercent up ON up.Id = pm.Id
INNER JOIN Updated_SalesReceiptItems u ON u.Id = up.Id
WHERE
(p.Price) % NULLIF(dbo.fn_Get_ProductPrice_By_Outlet(#OutletId, pm.ProductId), 0) = 0
AND [Percentage] = UpdatePercentage;
END;
Basically, I use nested common-table expressions to perform the same action as your original cursors, but these are now set-based. This means I can JOIN the results to the table to be updated and perform all of the updates in a single hit.
I almost certainly got some of this wrong, as I could see a number of parts in your original query that just seemed incorrect?

Count difference between distinct and join over distinct

How do I count the number of distinct rows minus a join over those same distinct rows?
I'm writing after triggers where I need to raise an error if the user does not have rights to the rows submitted. I can do this in two statements but this seems inefficient.
DECLARE #AccessibleCount INT =
(
SELECT
COUNT(DISTINCT i.[ParentId])
FROM
inserted i
INNER JOIN [SuperSecret].[Parent] AS p ON
p.[Id] = i.[ParentId] AND
p.[LockedBy] = #UserId
);
DECLARE #ActualCount INT = (SELECT COUNT(DISTINCT [ParentId]) FROM inserted);
IF (#AccessibleCount <> #ActualCount)
BEGIN
RAISERROR(...);
ROLLBACK TRANSACTION;
END
For performance sake, it seems like I should use a subquery over the distinct inserted.ParentId for both counts. I tried the following but it resulted in "Invalid object name 'i'."
DECLARE #ActualMinusAccessible INT =
(
SELECT
COUNT(*)
-
(
SELECT
COUNT(*)
FROM
i
INNER JOIN [SuperSecret].[Parent] AS p ON
p.[Id] = i.[ParentId] AND
p.[LockedBy] = #UserId
)
FROM
(
SELECT DISTINCT [ParentId] FROM inserted
) AS i
);
IF (#ActualMinusAccessible <> 0)
BEGIN
RAISERROR (...);
ROLLBACK TRANSACTION;
END
If am not wrong you want to Raise Error if a [ParentId] is inserted which is not present in [SuperSecret].[Parent] table. Try changing your SQL query like this.
IF EXISTS (SELECT 1
FROM inserted i
WHERE NOT EXISTS (SELECT 1
FROM [SuperSecret].[Parent] a
WHERE i.[ParentId] = a.[ParentId] AND a.[LockedBy] = #UserId))
BEGIN
RAISERROR (...);
ROLLBACK TRANSACTION;
END
OR
IF (SELECT Count(DISTINCT [ParentId]) - (SELECT Count(DISTINCT i.[ParentId])
FROM inserted i
INNER JOIN [SuperSecret].[Parent] AS p
ON p.[Id] = i.[ParentId]
AND p.[LockedBy] = #UserId)
FROM inserted) <> 0
BEGIN
RAISERROR (...);
ROLLBACK TRANSACTION;
END

Assigning a value to a variable inside (if exists) sql stored procedure

I am trying to write a stored procedure that checks if the patientID is exists based on patient name.
if exists (select pt.Id from dbo.Patients as pt where ltrim(RTRIM(pt.FirstName))=pt.FirstName and LTRIM(rtrim(pt.LastName))=pt.LastName)
begin
pID=select pt.Id from dbo.Patients as pt where ltrim(RTRIM(pt.FirstName))=pt.FirstName and LTRIM(rtrim(pt.LastName))=pt.LastName
else
--increment PatentID
Is there a way to not do select query two times
Just set the variable and then check if it's not null.
SELECT #pID = pt.Id from dbo.Patients as pt
WHERE ltrim(RTRIM(pt.FirstName))=pt.FirstName
AND LTRIM(rtrim(pt.LastName))=pt.LastName)
IF (#pID IS NULL)
BEGIN
--increment PatientID
SELECT #pID = MAX(Id) + 1 FROM dbo.Patients
-- insert new record
END
pID = select MAX(pt.Id) from dbo.Patients as pt where trim(RTRIM(pt.FirstName))=pt.FirstName and LTRIM(rtrim(pt.LastName))=pt.LastName;
IF #pID IS NULL
--Increment

Trigger, SQL SERVER 2008

i have two tables
PaymentData
Ser Customerid Totalpaid
1. AGP001 2400
2. AGP002 1000
3. AGP003 1500
Receipt
Receipt# Customerid Paid
1. AGP001 1200
2. AGP001 1200
I want to create a trigger on Receipt table, and trigger will fire on insert, update, and delete operations which updates the totalpaid field of PaymentData table. Everytime a new Receipt record is inserted or updated against some customerid, totalpaid field for that customer will updated as well.
Trigger should do following.
Update PaymentData.totalpaid = sum(Recipt.paid)
where Receipt.customerID = PaymentData.customerID
I guess you need some trigger like:
CREATE TRIGGER dbo.OnReceiptUpdate
ON dbo.Receipt
AFTER INSERT,DELETE,UPDATE --operations you want trigger to fire on
AS
BEGIN
SET NOCOUNT ON;
DECLARE #customer_id VARCHAR(10)
SET #customer_id= COALESCE
(
(SELECT customer_id FROM inserted), --table inserted contains inserted rows (or new updated rows)
(SELECT customer_id FROM deleted) --table deleted contains deleted rows
)
DECLARE #total_paid DECIMAL
SET #total_paid =
(
SELECT SUM(paid)
FROM Receipt
WHERE customer_id = #customer_id
)
UPDATE PaymentData
SET total_paid = #total_paid
WHERE customer_id = #customer_id
IF ##ROWCOUNT = 0 --if nothing was updated - you don't have record in PaymentData, so make it
INSERT INTO PaymentData (customer_id, total_paid)
VALUES (#customer_id, #total_paid)
END
GO
Keep in mind - it aint gonna work with multiply updates/deletes/inserts - This is just example of how you need to do it
Try this trigger with multiply updates, inserts or deletes.
CREATE TRIGGER [dbo].upd_PaymentData ON dbo.Receipt
FOR INSERT, UPDATE, DELETE
AS
IF ##ROWCOUNT = 0 return
SET NOCOUNT ON;
DECLARE #actionTable nvarchar( 10),
#insCount int = (SELECT COUNT(*) FROM inserted),
#delCount int = (SELECT COUNT(*) FROM deleted)
SELECT #actionTable = CASE WHEN #insCount > #delCount THEN 'inserted'
WHEN #insCount < #delCount THEN 'deleted' ELSE 'updated' END
IF #actionTable IN ('inserted', 'updated')
BEGIN
;WITH cte AS
(
SELECT r.Customerid, SUM(r.Paid) AS NewTotalPaid
FROM dbo.Receipt r
WHERE r.Customerid IN (SELECT i.Customerid FROM inserted i)
GROUP BY r.Customerid
)
UPDATE p
SET p.Totalpaid = c.NewTotalPaid
FROM dbo.PaymentData p JOIN cte c ON p.Customerid = c.Customerid
END
ELSE
BEGIN
;WITH cte AS
(
SELECT d.Customerid, SUM(ISNULL(r.Paid, 0)) AS NewTotalPaid
FROM deleted d LEFT JOIN dbo.Receipt r ON d.Customerid = r.Customerid
GROUP BY d.Customerid
)
UPDATE p
SET p.Totalpaid = c.NewTotalPaid
FROM dbo.PaymentData p JOIN cte c ON p.Customerid = c.Customerid
END
CREATE TRIGGER [dbo].upd_PaymentData ON dbo.Receipt
FOR INSERT, UPDATE, DELETE
AS
IF ##ROWCOUNT = 0 return
SET NOCOUNT ON;
DECLARE #actionTable nvarchar( 10),
#insCount int = (SELECT COUNT(*) FROM inserted),
#delCount int = (SELECT COUNT(*) FROM deleted)
SELECT #actionTable = CASE WHEN #insCount > #delCount THEN 'inserted'
WHEN #insCount < #delCount THEN 'deleted' ELSE 'updated' END
IF #actionTable IN ('inserted', 'updated')
BEGIN
;WITH cte AS
(
SELECT r.Customerid, SUM(r.Paid) AS NewTotalPaid,<strike> r.paymentDate</strike>
FROM dbo.Receipt r
WHERE r.Customerid IN (SELECT i.Customerid FROM inserted i)
GROUP BY r.Customerid
)
UPDATE p
SET p.Totalpaid = c.NewTotalPaid
<strike>SET p.lastpaymentDate = c.paymentDate</strike>
FROM dbo.PaymentData p JOIN cte c ON p.Customerid = c.Customerid
END
ELSE
BEGIN
;WITH cte AS
(
SELECT d.Customerid, SUM(ISNULL(r.Paid, 0)) AS NewTotalPaid
FROM deleted d LEFT JOIN dbo.Receipt r ON d.Customerid = r.Customerid
GROUP BY d.Customerid
)
UPDATE p
SET p.Totalpaid = c.NewTotalPaid
FROM dbo.PaymentData p JOIN cte c ON p.Customerid = c.Customerid
END
I tried to add more functionality as indicated inside tage but it gives error and is not working.
Simply added one field in both tables.
In paymentdata added lastpaymentdate,
And in receipt added paymentdate.
On inserting or updating receipt table, paymentdata.lastpaymentdate should also update to receipt.paymentdate.

SQL - Slow Cursor Simple Stored Procedure

I have a relatively simple stored procedure, but the amount of data in the transactions table is causing it to take forever to run. Any suggestions on a way I could either optimize the query or convert it to not use a cursor would be greatly appreciated.
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #ItemID uniqueidentifier
SET #CurrentCount = 0;
DECLARE #TempTransTable TABLE
(
ID uniqueidentifier
)
-- Insert statements for procedure here
DECLARE curs_GetAllItems CURSOR LOCAL STATIC FORWARD_ONLY READ_ONLY FOR
select
ID
from
item
where
locationid in
(
-- gets corona locations
select
Locations.ID
from Locations
left outer join unit
on unit.locationid = locations.id
where unit.unittype = '4'
)
and stat not in (1,10,11,13) -- only items not in stock
OPEN curs_GetAllItems
FETCH NEXT FROM curs_GetAllItems INTO #ItemID
WHILE (##FETCH_STATUS =0)
BEGIN
-- Clear table to ensure accurate data
DELETE FROM #TempTransTable
-- Insert transaction records to delete
-- Every transaction except the first two and the last
INSERT INTO #TempTransTable
select
ID
from
transactions
where
transactions.id not in
(select ID from (select top 2 * from transactions where itemid = #ItemID order by transdate asc) as t1
union
select ID from (select top 1 * from transactions where itemid = #ItemID order by transdate desc) as t2)
and itemid = #ItemID
-- Delete trans records
DELETE FROM
dbo.transactions
WHERE
transactions.ID in (select ID from #TempTransTable);
-- Get next item.id
FETCH NEXT FROM curs_GetAllItems INTO #ItemID
END
CLOSE curs_GetAllItems
DEALLOCATE curs_GetAllItems
END
;with tmp as (
select *,
rn_asc = ROW_NUMBER() over (partition by t.itemid order by transdate asc),
rn_desc = ROW_NUMBER() over (partition by t.itemid order by transdate desc)
from transactions t
where exists (
select *
from item i
join Locations l on i.locationid = l.ID
join unit u on u.locationid = l.id and u.unittype = '4'
where i.id = t.itemid)
and stat not in (1,10,11,13) -- only items not in stock
)
delete tmp
where rn_asc > 2 and rn_desc > 1;
I think you should review your approach.
It should be possible to manage without cursor and loop at all.
May be you need use intermediate temporary table (consider adding indexes to it).
Other point: if those queries are hard and they are working in database with concurrent changes, you can easy get into problems - locks, timeouts and inconsistent data.