ERROR : Using EXEC and Select in Same Procedure - sql

i have the following statements for my procedure
DECLARE #StockA table (GoodID int,NominalQty decimal(16,2),ActualQty decimal(16,2))
INSERT INTO #StockA(GoodID,NominalQty,ActualQty)
EXEC ('INV.[usp_GetStorageAvailability] ' + #SysYear +',"2015-01-01",1')
UPDATE #ReqItems
SET Stock = S.ActualQty , Rem = (S.ActualQty - R.Qty) FROM #ReqItems R JOIN #StockA S on R.GoodID = S.GoodID
DECLARE #HasMinus tinyint
SET #HasMinus = (SELECT TOP 1 * FROM #ReqItems WHERE Rem < 0)
IF #HasMinus > 0 begin
DECLARE #GC nvarchar(10)
SET #GC = (SELECT TOP 1 GoodCode FROM #ReqItems WHERE Rem < 0)
SET #Outcome = 0
SET #Descr = 'XZY'
end
SELECT #Outcome,#Descr
and i get the following error :
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
what should i do ?
thanks

The problem is here:
SET #HasMinus = (SELECT TOP 1 * FROM #ReqItems WHERE Rem < 0)
I think the error is pretty clear. The * refers to multiple columns. You need to choose one:
SET #HasMinus = (SELECT TOP 1 ?? FROM #ReqItems WHERE Rem < 0);
I'm not sure which column you want to select, however.
If you just want a count, you can do:
SELECT #HasMinus = COUNT(*) FROM #ReqItems WHERE Rem < 0;
This might be the intention of your code. (Note that the SET isn't necessary.)

Related

SQL Server: is this a bug or do I have a misunderstanding?

Today I'm found a very sticky problem on SQL Server 2014.
Scenario: I want to pay awards to my customer (some pin code for cell phone operator)
In last cycle of loop T.Used = 0 condition is bypassed and is not working. I know in other conditions in that query (T.Cash < (#myAwards - #paid)) is there a mistake and I must to use T.Cash <= (#myAwards - #paid) instead of this but please focus on main question.
Why it's happened when I update Used flag to 1 (True) then in next loop it's selected while it doesn't have a valid condition (T.Used = 0)?
DECLARE #myAwards INT = 90000,
#paid INT = 0;
DECLARE #Temp TABLE
(
Id INT NOT NULL,
Pin VARCHAR(100) NOT NULL,
Cash INT NOT NULL,
[Weight] INT NULL,
Used BIT NOT NULL
)
INSERT INTO #Temp
SELECT
UPFI.Id, UPFI.PinCode,
PT.Cash, NULL, 0
FROM
dbo.UploadedPinFactorItem UPFI WITH (NOLOCK)
INNER JOIN
dbo.PinType PT WITH (NOLOCK) ON PT.ID = UPFI.PinTypeID
WHERE
PT.Cash <= #myAwards
UPDATE T
SET [Weight] = ISNULL((SELECT COUNT(TT.Id)
FROM #Temp TT
WHERE TT.Cash = T.Cash), 0) * T.Cash
FROM #Temp T
--For debug (first picture)
SELECT * FROM #Temp
DECLARE #i int = 1
DECLARE #count int = 0
SELECT #count = COUNT([Id]) FROM #Temp C WHERE C.Used = 0
WHILE (#i <= #count AND #paid < #myAwards)
BEGIN
DECLARE #nextId INT,
#nextCash INT,
#nextFlag BIT;
-- 'T.Used = 0' condition is by passed
SELECT TOP (1)
#nextId = T.Id, #nextCash = T.Cash, #nextFlag = T.Used
FROM
#Temp T
WHERE
T.Used = 0
AND T.Cash < (#myAwards - #paid)
ORDER BY
T.[Weight] DESC, T.Cash DESC, T.Id DESC
UPDATE #Temp
SET Used = 1
WHERE Id = #nextId
SET #i = #i + 1
SET #paid = #paid + #nextCash
--Show result in second picture
SELECT
#i AS 'i', #paid AS 'paid', #nextFlag AS 'flag', #nextId AS 'marked Id',*
FROM
#temp T
ORDER BY
T.[Weight] DESC, T.Cash DESC, T.Id DESC
END
SELECT 'final', #paid, *
FROM #temp T
ORDER BY T.[Weight] DESC, T.Cash DESC, T.Id DESC
Please let me to understand this is a bug or I have misunderstanding
First screenshot:
Second screenshot (result of loop):
Third screenshot (final result):
As per my comments:
This isn't a problem with the condition, the problem is with the implemented logic. After i = 4, there are no more rows where T.Used = 0 AND T.Cash < (#myAwards - #paid), that makes it so your reassigning variables gets zero rows, so they mantain the previous values.
You can test this behavior by doing:
DECLARE #A INT = 10;
SELECT #A = object_id
FROM sys.all_objects
WHERE name = 'an object that doesn''t exist'
SELECT #A;

Less expensive query?

I have a stored procedure that returns an integer 1 or 0 depending on specific criteria. It currently uses three select statements and it will be used heavily by multiple users across multiple locations. There has to be a more efficient way of doing this.
In short the query checks first to see if all checklist items on an order are completed (a separate table), then it checks to see if a field named BreakOutGuest (a bit field) is a 1 or 0. Depending on that result it checks to see if the total guest count is greater than 0 and the order total is zero. It returns the one or zero on all this criteria. Is there a more efficient way to do this? A temp table so I only have to hit the actual tables once? Below is the code.
#ORDERID INT
AS
BEGIN
DECLARE #AUTO_CLOSE INT
SET NOCOUNT ON;
--If all checklist items are marked complete move on, if not set #AUTO_CLOSE=0
IF NOT EXISTS(SELECT ORDERID FROM dbo.orderchecklistitems WHERE OrderID=#ORDERID AND CompletedON IS NULL)
BEGIN
--if BreakOutGuestFees is 1 only sum Guest_Count_1 + Guest_Count_2
IF EXISTS(SELECT * FROM dbo.Orders WHERE (GuestCount_1 + GuestCount_2)>1 AND OrderTotal=0 AND BreakoutGuestFees=1)
BEGIN
SET #AUTO_CLOSE=1
END
ELSE
SET #AUTO_CLOSE=0
--if BreakOutGuestFees is 0 only consider Guest_Count_1
IF EXISTS(SELECT * FROM dbo.Orders WHERE (GuestCount_1)>1 AND OrderTotal=0 AND BreakoutGuestFees=0)
BEGIN
SET #AUTO_CLOSE=1
END
ELSE
SET #AUTO_CLOSE=0
END
ELSE
SET #AUTO_CLOSE=0
END
If am not wrong you can combine two if clause into single if clause by using AND , OR logic. Try this.
IF NOT EXISTS(SELECT ORDERID
FROM dbo.orderchecklistitems
WHERE OrderID = #ORDERID
AND CompletedON IS NULL)
BEGIN
IF EXISTS(SELECT *
FROM dbo.Orders
WHERE ( ( GuestCount_1 + GuestCount_2 > 1
AND BreakoutGuestFees = 1 )
OR ( BreakoutGuestFees = 0
AND GuestCount_1 > 1 ) )
AND OrderTotal = 0
AND OrderID = #ORDERID)
SET #AUTO_CLOSE=1
ELSE
SET #AUTO_CLOSE=0
END
ELSE
SET #AUTO_CLOSE=0
You can perform your selection check with only one query
SELECT
(SELECT sum(1) FROM dual WHERE EXISTS (SELECT ORDERID FROM dbo.orderchecklistitems WHERE OrderID=#ORDERID AND CompletedON IS NULL)),
(SELECT sum(1) FROM dual WHERE EXISTS (SELECT 1 FROM dbo.Orders WHERE (GuestCount_1 + GuestCount_2)>1 AND OrderTotal=0 AND BreakoutGuestFees=1)),
(SELECT sum(1) FROM dual WHERE EXISTS (SELECT 1 FROM dbo.Orders WHERE (GuestCount_1)>1 AND OrderTotal=0 AND BreakoutGuestFees=0))
INTO
result1, result2, result3
from dual
then check results
DELCARE #AUTO_CLOSE INT = 0
IF NOT EXISTS(SELECT ORDERID
FROM dbo.orderchecklistitems
WHERE OrderID = #ORDERID
AND CompletedON IS NULL)
BEGIN
SET #AUTO_CLOSE =
(
SELECT
CASE
WHEN (GuestCount_1 + GuestCount_2 > 1) AND BreakoutGuestFees = 0 THEN 1
WHEN (GuestCount_1 > 1 ) AND BreakoutGuestFees = 1 THEN 1
ELSE 0 END
FROM dbo.orders
WHERE OrderTotal = 0 AND OrderID = #orderID
)
END

SQL Server Express stored procedure condition checking

I have written a stored procedure:
ALTER PROCEDURE [dbo].[GetBRs_Pager]
#PageIndex INT
,#PageSize INT
,#SupId INT
,#RecordCount INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SELECT ROW_NUMBER()
OVER (
ORDER BY [tblBR].[ID] ASC
) AS RowNumber
,[tblBR].[ID]
,[tblBR].[BRName]
,[tblBR].[SupervisorId]
,[tblSupervisor].[SupervisorName]
,[tblBR].[BRCode]
,[tblBR].[BRMobile]
,[tblBR].[BRTypeId]
,[tblType].[TypeName]
,[tblBR].[BRImageUrl]
INTO #Results
FROM [tblBR]
INNER JOIN [tblType]
ON [tblBR].[BRTypeId] = [tblType].[ID]
INNER JOIN [tblSupervisor]
ON [tblBR].[SupervisorId] = [tblSupervisor].[ID]
where [tblBR].[Active] = 1
and [tblBR].[SupervisorId]=#SupId
SELECT #RecordCount = COUNT(*)
FROM #Results
SELECT * FROM #Results
WHERE RowNumber BETWEEN(#PageIndex -1) * #PageSize + 1 AND(((#PageIndex -1) * #PageSize + 1) + #PageSize) - 1
DROP TABLE #Results
END
Now I want to modify the query as follows
if(#supId != 0) then where [tblBR].[Active] = 1
and [tblBR].[SupervisorId] = #SupId else [tblBR].[Active] = 1
How to do it? Anyone helps me is greatly appreciated. Thanks in advance.
Just basic logic, I think:
where [tblBR].[Active] = 1
and (
[tblBR].[SupervisorId]=#SupId or
#SupId = 0
)
You don't need control-flow statements or the like here. You just need to express the alternatives and link them together using boolean operators.
What I would do is the following thing, adding the following code after your where clauses
AND [tblBR].[Active] = 1
AND [tblBR].[SupervisorId] = CASE WHEN #supId != 0 THEN #supId ELSE [tblBR].[SupervisorId] END
the second and check if the variable if different than 0, in that case it filter the supervisorid with your variable, if equal 0 it filter the supervisorid field by his row value.

SELECT TOP 1, 2, 3 RowId from TableWithNRows

Stupid problem and the answer may not exists (Gasp! Groan! Gnash! Teeth!)
I have a select:
SELECT * FROM SockDrawer WHERE Color = 'red'
This results in 3 rows with row ids of 33896, 35901, 37903
(A lot of other results too.)
What I want is something like this:
DECLARE #ROWID INT --- HOW DO I USE ARRAYS? I'll google but an example would help.
DECLARE #COUNT INT
DECLARE #LIMIT INT
SELECT * FROM SockDrawer WHERE Color = 'red'
--(Returns 3 rows. With 3 RowId's 33896, 35901, 33896)
SET #LIMIT = ##ROWCOUNT
SET #COUNT = 1
WHILE #COUNT < #LIMIT BEGIN
SET #ROWID[0] = (SELECT SockKey From SockDrawer WHERE RowID = 33896)
SET #ROWID[1] = (SELECT SockKey From SockDrawer WHERE RowID = 35901)
SET #ROWID[2] = (SELECT SockKey From SockDrawer WHERE RowID = 33896)
SET #Count = #Count + 1
END
GO
Then I need to:
SET #COUNT = 0
WHILE #COUNT < #LIMIT
BEGIN
DELETE FROM SockDrawer WHERE RowID = #ROWID[#COUNT]
END
GO
The trick is I'll never know if I'm dealing with 1 row to delete or 50.
I'm a dork. I can change. If I want to.
If you don't care about which sock is left for each color, then:
;WITH x AS
(
SELECT *, rn = ROW_NUMBER() OVER (PARTITION BY Color ORDER BY RowID)
FROM dbo.SockDrawer
)
DELETE x WHERE rn > 1;
If you care which one you keep, adjust the ORDER BY accordingly.

SQL Stored Procedure set variables using SELECT

I have a stored procedure in SQL Server 2005 with multiple variables and I want to set the values of these variables using a select statement. All three variables come from a same table and there should be a way to set them using one select statement instead of the way I currently have as shown below. Please help me to figure it out.
DECLARE #currentTerm nvarchar(max)
DECLARE #termID int
DECLARE #endDate datetime
SET #currentTerm =
(
Select CurrentTerm from table1 where IsCurrent = 1
)
SET #termID =
(
Select TermID from table1 where IsCurrent = 1
)
SET #endDate =
(
Select EndDate from table1 where IsCurrent = 1
)
select #currentTerm = CurrentTerm, #termID = TermID, #endDate = EndDate
from table1
where IsCurrent = 1
One advantage your current approach does have is that it will raise an error if multiple rows are returned by the predicate. To reproduce that you can use.
SELECT #currentTerm = currentterm,
#termID = termid,
#endDate = enddate
FROM table1
WHERE iscurrent = 1
IF( ##ROWCOUNT <> 1 )
BEGIN
RAISERROR ('Unexpected number of matching rows',
16,
1)
RETURN
END