Subquery in an IN() clause causing error - sql-server

I'm on SQL Server 2005 and I am getting an error which I am pretty sure should not be getting.
Msg 512, Level 16, State 1, Procedure spGetSavedSearchesByAdminUser, Line 8 Subquery
returned more than 1 value. This is not permitted when the subquery
follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
I am following the example# B on
this MSDN link.
My stored proc code is as follows. I can simplify it for the sake of this post if you request so:
ALTER PROCEDURE [dbo].[spGetSavedSearchesByAdminUser]
#strUserName varchar(50)
,#bitQuickSearch bit = 0
AS
BEGIN
SELECT [intSearchID] ,strSearchTypeCode ,[strSearchName]
FROM [tblAdminSearches]
WHERE
strUserName = #strUserName
AND
strSearchTypeCode
IN (
CASE #bitQuickSearch
WHEN 1 THEN 'Quick'
ELSE (SELECT strSearchTypeCode FROM tblAdvanceSearchTypes)
END
)
ORDER BY strSearchName
END
I have checked there is no datatype mismatch between the resultset from the subquery and the strSearchTypeCode the subquery result is compared with.
I see no reason why this should not work. If you have any clues then please let me know.

Try rearranging the query so that the boolean expression occurs inside the subselect, e.g.
ALTER PROCEDURE [dbo].[spGetSavedSearchesByAdminUser]
#strUserName varchar(50)
,#bitQuickSearch bit = 0
AS
BEGIN
SELECT [intSearchID] ,strSearchTypeCode ,[strSearchName]
FROM [tblAdminSearches]
WHERE
strUserName = #strUserName
AND
strSearchTypeCode
IN (SELECT strSearchTypeCode FROM tblAdvanceSearchTypes where #bitQuickSearch=0
UNION
SELECT 'Quick' AS strSearchTypeCode WHERE #bitQuickSearch=1)
ORDER BY strSearchName
END

I don't know that you can use the CASE statement inside of an IN clause like that. I'd suggest rewriting that bit to:
WHERE strUserName = #strUserName AND (
(#bitQuickSearch = 1 AND strSearchTypeCode = 'Quick')
OR
(strSearchTypeCode IN (SELECT strSearchTypeCode FROM tblAdvanceSearchTypes))
)
or, if you really like the style you got there:
WHERE strUserName = #strUserName
AND strSearchTypeCode IN (
SELECT CASE #bitQuickSearch WHEN 1 THEN 'Quick' ELSE strSearchTypeCode END
FROM tblAdvanceSearchTypes
)
In general, SQL should be smart to smart enough to optimize away the table if #bitQuickSearch = 1. But, I'd check the query plan just to be sure (trust, but verify).

It seems to me that this SELECT:
SELECT strSearchTypeCode FROM tblAdvanceSearchTypes
returns multiple rows, and that is your problem. You can rewrite it to be:
SELECT TOP 1 strSearchTypeCode FROM tblAdvanceSearchTypes

Related

SQL Merge (with Temp Table) failing on SubQuery returned more than 1 value (But I am not using a Sub-Query.. in the update)

I am hoping someone can help me out of this tedium...!?
As the title suggests I have a Temp Table (create dynamically in a select statement):
SELECT *
INTO #results
FROM Table_1
CROSS APPLY ( SELECT TOP 1 *
FROM Table_2
WHERE (Table_1.ItemId = Table_2.ItemId)
ORDER BY CreatedDt DESC
)
... which as you can see uses a Sub-Query in a cross join.
Next I am trying to use this temp table #results to update a related table with its values. have tried using an update:
UPDATE a
SET a.StatusId = b.StatusId
FROM Table_1 a
INNER JOIN #results b on (a.ItemId = b.ItemId)
and with a Merge:
MERGE INTO Table_1 a
USING #results b
ON (a.ItemId = b.temId)
WHEN MATCHED THEN UPDATE SET a.StatusId = b.StatusId;
but I seem to always get a response:
Msg 512, Level 16, State 1, Procedure trg_dbo_PBITree_TreeModel_HierarchicalEscalationHistory_InsertNode,
Line 7 [Batch Start Line 11] Subquery returned more than 1 value. This
is not permitted when the subquery follows =, !=, <, <= , >, >= or
when the subquery is used as an expression.
When I query the two tables in question (#results & Table_1) they both have 1 to 1 relationships and cannot see at all where it could be hiding some kind of Subquery!?
Can anyone help quickly on this at all please? This seems to be 1-0-1 stuff and its baking my burger!
-- Edit --
I have taken a look at the Trigger mentioned in the error message as it was suggested it could be trying to handle a single row update instead of a multiple row update which is what I am doing. Nothing looking too unusual to me...?
ALTER TRIGGER [dbo].[trg_dbo_PBITree_TreeModel_HierarchicalEscalationHistory_InsertNode]
ON [dbo].[Table_1]
AFTER UPDATE
AS
BEGIN
-- NodeModelInsertOrUpdateTPH
IF ((select [Item] from inserted) = 'X')
BEGIN
UPDATE tx
SET
tx.LastUpdatedBy = i.LastUpdatedBy,
tx.LastUpdatedAt = i.LastUpdatedAt
FROM
[dbo].[Table_X] tx,
inserted i
WHERE
tx.OtherItemId = i.OtherItemId
END
END
Anyone have any ideas?
Your trigger is the issue here. Your IF statement has a query which would return more than 1 row and that exact message would be the result. You should make your trigger tolerant of multiple row operations. Here is the same logic but it can handle any number of rows being updated.
ALTER TRIGGER [dbo].[trg_dbo_PBITree_TreeModel_HierarchicalEscalationHistory_InsertNode]
ON [dbo].[Table_1]
AFTER UPDATE
AS
BEGIN
UPDATE tx
SET
tx.LastUpdatedBy = i.LastUpdatedBy,
tx.LastUpdatedAt = i.LastUpdatedAt
FROM
[dbo].[Table_X] tx
join inserted i ON tx.OtherItemId = i.OtherItemId
where i.Item = 'X'
END

Update DATEADD with nvarchar?

I am stuck with a table that has a column, [Renewal] that was set as a nvarchar(255),null. Not my handiwork and I cant change it. I now need to use this column in a calculation. Below as close as I can get with doing it in one statement but I cant get past the "incorrect syntax near..." error after the ELSE statement. If it ran into an issue trying to CAST as INT I would want it to continue with the rest of the records and not hose the whole query. I suppose I could add an additional column thats INT, not null and write an update statement to run prior to this but I fear I would run to the same issue. I am more UI guy than SQL guy, any suggestions? It would run on SQL Server 2008R. Thank you in advance
UPDATE C
SET [ExpirationDate] = DATEADD(MONTH,
(CASE WHEN ISNUMERIC(C.Renewal)= 1
THEN CAST(C.Renewal AS INT)
ELSE 0)
, [ExpirationDate])
FROM dbo.MyTable C
WHERE C.MyCondition = 'True'
You are missing END in CASE statement
UPDATE C
SET [ExpirationDate] = Dateadd(MONTH, ( CASE
WHEN Isnumeric(C.Renewal) = 1 THEN Cast(C.Renewal AS INT)
ELSE 0
END ), [ExpirationDate])
FROM dbo.MyTable C
WHERE C.MyCondition = 'True'
Filter the records with only numeric values and apply DATEADD function instead of adding 0 months
Also ISNUMERIC is not preferred. ISNUMERIC returns 1 if the string can be converted to any one of ints, numeric/decimal, float, or money.
https://connect.microsoft.com/SQLServer/feedback/details/302466/isnumeric-returns-true-for-and
UPDATE C
SET [ExpirationDate] = DATEADD(MONTH,CAST(C.Renewal AS INT), [ExpirationDate])
FROM dbo.MyTable C
WHERE C.MyCondition = 'True'
AND C.Renewal NOT LIKE '%[^0-9]%'
This would be more straightforward.
UPDATE C
SET [ExpirationDate] = DATEADD(MONTH, CAST(c.Renewal AS INT), [ExpirationDate])
FROM dbo.MyTable C
WHERE C.MyCondition = 'True'
AND ISNUMERIC(c.Renewal) = 1

Is it possible to have a view in sql-server 2008 that splits one row into many?

The back story is I am trying to write a view that takes a table who's every row is an ID and serialized data for that ID in a clob and presents it in sql navigable form. basically my code looks like:
CREATE VIEW UNSERIALIZED_TABLE_VIEW AS
SELECT
SOURCE_TABLE.ID SOURCE_ID,
a.*
FROM
SOURCE_TABLE,
FUNCTION_WHICH_UNSERIALIZES((SELECT DATA FROM SOURCE_TABLE WHERE ID = SOURCE_ID)
I tried putting the function in the select statement, but that just gave a syntax error about it being undefined. When it runs the error is usually about a subquery returning too many values. I could just unserialize the data in batches, but now I'm really curious what's going wrong.
Example Data
#0History:23:ALPHANUMERICSTUFF1234567ID:11:ACCT1234567SourceMode:6:ANNUAL.ModeChanges:UniqueIndex:23:ALPHANUMERICSTUFF1234567ID:11:ACCT1234567OldValue:1:+NewValue:6:ANNUALChangeType:1:AChangeDate:20:6/03/2013 2:49:32 AM.
#0History:UniqueIndex:95:NOTTHESAME0987654|ALPHANUMERIC534|PRETEND349235|95CHARACTERSID:47:GNR44718500|PNR48CDQ704|PGP48090798|FGDS2345236SourceMode:26:ANNUAL|C-P-D|ANNUAL|ANNUALLoan:3:|||ModeChanges:UniqueIndex:95:00487SOMETHING4264500ORD|992581PROBABLY04ORD|0048SHOULD238BET|0095CHARS436PR638FGP07VDCID:47:GNR44718500|PNR48CDQ704|PGP48090798|FGDS2345236OldValue:7:+|+|+|+NewValue:26:ANNUAL|C-P-D|ANNUAL|ANNUALChangeType:7:A|A|A|AChangeDate:91:12/22/2013 11:53:11 PM|4/22/2013 11:53:11 PM|12/22/2013 11:53:11 PM|12/22/2013 11:53:11 PM.
The data is serialized table data of the form COLUMN_NAME:LENGTH_OF_ENTRY:DATA_FOR_COLUMN_ROW_1|DATA_FOR_COLUMN_ROW2|....NEXT_COLUMN_NAME...
Example of Function:
CREATE FUNCTION FUNCTION_THAT_UNSERIALIZES (#clob varchar(max),#colname varchar(max)) RETURNS #NewValue TABLE (ID INT,value varchar(max)) AS
BEGIN
DECLARE #colstart INT,#lenstart INT,#lenend INT,#collen VARCHAR(MAX),#lngth INT,#tmp VARCHAR(MAX), #rowid INT,#value VARCHAR(max),#next INT;
SELECT
#colstart = CHARINDEX(#colname,#tmp)+1,
#lenstart = CHARINDEX(':',#tmp,#colstart)+1,
#lenend = CHARINDEX(':',#tmp,#lenstart),
#collen = SUBSTRING(#tmp,#lenstart,#lenend - #lenstart),
#lngth = CAST (#collen AS INT),
#tmp = SUBSTRING(#tmp,#lenend,#lngth);
WHILE LEN(#tmp) > 0 BEGIN
SET #next = CHARINDEX('|',#tmp);
IF #next > 0 BEGIN
SET #value = SUBSTRING(#tmp,0,#next);
SET #tmp = SUBSTRING(#tmp,#next+1,LEN(#tmp) - #next);
END ELSE BEGIN
SET #value = #tmp;
SET #tmp = '';
END
INSERT INTO #NewValue VALUES(#rowid,#value)
SET #rowid = #rowid+1;
END
RETURN
Example Error
Msg 512, Level 16, State 1, Line 7
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
Msg 4104, Level 16, State 1, Line 15
The multi-part identifier "SOURCE_TABLE.SOURCE_ID" could not be bound.
.. I think there might have been another one, but can't figure out how to reproduce it right this minute.
I think this might be the syntax you need to accomplish what I think you are trying to do.
CREATE VIEW UNSERIALIZED_TABLE_VIEW AS
SELECT
SOURCE_TABLE.ID SOURCE_ID,
a.*
FROM SOURCE_TABLE
CROSS APPLY FUNCTION_WHICH_UNSERIALIZES(DATA, #colname) a
I'm not certain what your #colname parameter should be; it is left out of your code in the question.

Stored procedure throws an error on my subquery

My database trigger takes a date from a column and adds 60 days to it and stores it into another column.
And it does as expected when I execute the code in query window and it throws the following error.
Msg 512, Level 16, State 1, Line 4
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
My code:
DECLARE #NextDate date
SELECT #NextDate = (SELECT DATEADD(day, 10, Today) FROM Test)
INSERT INTO Test (Notes, Today)
VALUES ('Testing in Query2', GETDATE())
DECLARE #newint int
SET #newint = SCOPE_IDENTITY()
UPDATE Test
SET Someday = #NextDate
WHERE ID = #newint
RESULT
But keeps giving the error with the result.
Msg 512, Level 16, State 1, Line 4
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
INSERT INTO #NextDate SELECT DATEADD(day,10,Today) FROM Test
In Your Query
SELECT #NextDate = (SELECT DATEADD(day,10,Today) FROM Test)
the sub query returning more than one value and you cant assign the multiple values to one variable. this is causing the problem in you query.
As Dominic Deepan.d Suggested use the where condition
SELECT #NextDate = (SELECT DATEADD(day,10,Today) FROM Test WHERE ID = #newint)
Or else try the same in another way
SELECT #NextDate = DATEADD(day,10,Today) FROM Test WHERE ID = #newint
Well Finally i sorted it out, Silly me :D
INSERT INTO Test(Notes,Today)
values ('Testing in Query3',GETDATE())
DECLARE #newint int
SET #newint = SCOPE_IDENTITY()
DECLARE #NextDate date
SELECT #NextDate = (SELECT DATEADD(day,10,Today) FROM Test WHERE ID = #newint)
UPDATE Test
SET Someday = #NextDate
WHERE ID = #newint
GO
I juz had to put WHERE in this line
SELECT #NextDate = (SELECT DATEADD(day,10,Today) FROM Test WHERE ID = #newint)

MSSQL update multiple rows based on select statement

I am trying to update multiple rows in one table, based on a select statement on another table.
This is my query:
UPDATE dbo.[user_message_content]
SET [status] = 1
WHERE [message_id] = (SELECT [message_id] FROM dbo.[user_message] WHERE [receiver_id] = #userID)
AND [status] = 0
This select statement may return multiple rows, which leads me to this error:
Msg 512, Level 16, State 1, Procedure usp_profileUserMessageMarkAsRead, Line 11
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
What is the correct way to achieve what I need?
Thanks very much
If you want to update all those records, change the = to IN:
UPDATE dbo.[user_message_content]
SET [status] = 1
WHERE [message_id] IN
( SELECT [message_id] FROM dbo.[user_message] WHERE [receiver_id] = #userID )
AND [status] = 0
You can also use UPDATE with FROM clause http://msdn.microsoft.com/en-us/library/aa260662(SQL.80).aspx.
USE tempdb;
GO
create table #user_message_content([status] int, message_id int)
create table #user_message (message_id int,receiver_id int)
declare #UserID int
UPDATE mc
SET mc.[status] = 1
FROM #user_message_content mc join #user_message m on mc.message_id = m.message_id
WHERE m.receiver_id = #userID
AND mc.[status]=0;
drop table #user_message_content
drop table #user_message
I think you need to use a join to do this
USE dbo; // Sets the current database to dbo, I did this for readability
UPDATE user_message_content join user_message on user_message_content.message_id = user_message.message_id
SET user_message_content.status = 1
WHERE user_message.receiver_id = #userID;

Resources