101

I run the following query:

SELECT 
   orderdetails.sku,
   orderdetails.mf_item_number,
   orderdetails.qty,
   orderdetails.price,
   supplier.supplierid,
   supplier.suppliername,
   supplier.dropshipfees,
   cost = (SELECT supplier_item.price
           FROM   supplier_item,
                  orderdetails,
                  supplier
           WHERE  supplier_item.sku = orderdetails.sku
                  AND supplier_item.supplierid = supplier.supplierid)
FROM   orderdetails,
       supplier,
       group_master
WHERE  invoiceid = '339740'
       AND orderdetails.mfr_id = supplier.supplierid
       AND group_master.sku = orderdetails.sku  

I get the following error:

Msg 512, Level 16, State 1, Line 2 Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.

Any ideas?

TT.
  • 15,774
  • 6
  • 47
  • 88
Anilkumar
  • 1,107
  • 3
  • 10
  • 11
  • 44
    Oh and stop using the implied join syntax, It is a very poor practice, harder to maintain and easier to make mistakes with. – HLGEM Apr 16 '10 at 13:34
  • 6
    @HLGEM why is it poor practice, harder to maintain and easier to make mistakes with? – reggaeguitar Apr 19 '19 at 15:20
  • 8
    What fields are these tables joined on? Hint: the fact that I can't tell is the problem. – naughtilus Aug 05 '19 at 07:59
  • 1
    @reggaeguitar - Personally I find implied joins more difficult to understand. I actually have to stop and pay attention to the `WHERE` clause to ensure that the joins are implemented properly. If the implied conditions are scattered throughout the clause, it becomes even harder and degrades readability. As with all programming languages, semantic usage is key to making your SQL more legible - all joins should be properly defined as a subsection of the `FROM` clause - that way, you know where your data is coming from, and more importantly, *why*. – Paul Mar 31 '22 at 09:31

12 Answers12

62

Try this:

SELECT
    od.Sku,
    od.mf_item_number,
    od.Qty,
    od.Price,
    s.SupplierId,
    s.SupplierName,
    s.DropShipFees,
    si.Price as cost
FROM
    OrderDetails od
    INNER JOIN Supplier s on s.SupplierId = od.Mfr_ID
    INNER JOIN Group_Master gm on gm.Sku = od.Sku
    INNER JOIN Supplier_Item si on si.SKU = od.Sku and si.SupplierId = s.SupplierID
WHERE
    od.invoiceid = '339740'

This will return multiple rows that are identical except for the cost column. Look at the different cost values that are returned and figure out what is causing the different values. Then ask somebody which cost value they want, and add the criteria to the query that will select that cost.

Jeffrey L Whitledge
  • 58,241
  • 9
  • 71
  • 99
55

Check to see if there are any triggers on the table you are trying to execute queries against. They can sometimes throw this error as they are trying to run the update/select/insert trigger that is on the table.

You can modify your query to disable then enable the trigger if the trigger DOES NOT need to be executed for whatever query you are trying to run.

ALTER TABLE your_table DISABLE TRIGGER [the_trigger_name]

UPDATE    your_table
SET     Gender = 'Female'
WHERE     (Gender = 'Male')

ALTER TABLE your_table ENABLE TRIGGER [the_trigger_name]
Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
jk.
  • 14,365
  • 4
  • 43
  • 58
  • 6
    Wouldn't it be better to fix the trigger(s) rather than disabling them? Those triggers were created for a reason, no? You might bypass some important functionality by disabling the trigger(s)... – TT. Nov 12 '16 at 07:13
  • 4
    @TT. Yes, but please see the bolded text in the answer. You can modify your query to disable then enable the trigger if the trigger DOES NOT need to be executed for whatever query you are trying to run. – jk. Nov 14 '16 at 22:47
  • 1
    Altering the table during the query is absolutely terrible. If you need to skip the trigger, do it on a per-connection basis. – Ben Voigt Sep 26 '18 at 14:46
35
SELECT COLUMN 
    FROM TABLE 
WHERE columns_name
    IN ( SELECT COLUMN FROM TABLE WHERE columns_name = 'value');

note: when we are using sub-query we must focus on these points:

  1. if our sub query returns 1 value in this case we need to use (=,!=,<>,<,>....)
  2. else (more than one value), in this case we need to use (in, any, all, some )
Leniel Maccaferri
  • 100,159
  • 46
  • 371
  • 480
Diyar sereroy
  • 351
  • 3
  • 2
13
cost = Select Supplier_Item.Price from Supplier_Item,orderdetails,Supplier 
   where Supplier_Item.SKU=OrderDetails.Sku and 
      Supplier_Item.SupplierId=Supplier.SupplierID

This subquery returns multiple values, SQL is complaining because it can't assign multiple values to cost in a single record.

Some ideas:

  1. Fix the data such that the existing subquery returns only 1 record
  2. Fix the subquery such that it only returns one record
  3. Add a top 1 and order by to the subquery (nasty solution that DBAs hate - but it "works")
  4. Use a user defined function to concatenate the results of the subquery into a single string
Mayo
  • 10,544
  • 6
  • 45
  • 90
  • 5
    On 3; ALL **competent** developers should also hate that. There was a question on 'Pet Peeves' a while back; and mine would be: "Just because there isn't an error messages, doesn't mean that it 'works'!". That said, you can add #5: Restructure the entire query; i.e. instead of getting customer and 'looking-up' invoices; rather get invoices and 'lookup' customers. – Disillusioned Apr 16 '10 at 13:59
11

The fix is to stop using correlated subqueries and use joins instead. Correlated subqueries are essentially cursors as they cause the query to run row-by-row and should be avoided.

You may need a derived table in the join in order to get the value you want in the field if you want only one record to match, if you need both values then the ordinary join will do that but you will get multiple records for the same id in the results set. If you only want one, you need to decide which one and do that in the code, you could use a top 1 with an order by, you could use max(), you could use min(), etc, depending on what your real requirement for the data is.

mofoe
  • 3,634
  • 1
  • 16
  • 16
HLGEM
  • 94,695
  • 15
  • 113
  • 186
11

I had the same problem , I used in instead of = , from the Northwind database example :

Query is : Find the Companies that placed orders in 1997

Try this :

SELECT CompanyName
    FROM Customers
WHERE CustomerID IN (
                        SELECT CustomerID 
                            FROM Orders 
                        WHERE YEAR(OrderDate) = '1997'
                    );

Instead of that :

SELECT CompanyName
    FROM Customers
WHERE CustomerID =
(
    SELECT CustomerID 
        FROM Orders 
    WHERE YEAR(OrderDate) = '1997'
);
JAN
  • 21,236
  • 66
  • 181
  • 318
10

Either your data is bad, or it's not structured the way you think it is. Possibly both.

To prove/disprove this hypothesis, run this query:

SELECT * from
(
    SELECT count(*) as c, Supplier_Item.SKU
    FROM Supplier_Item
    INNER JOIN orderdetails
        ON Supplier_Item.sku = orderdetails.sku
    INNER JOIN Supplier
        ON Supplier_item.supplierID = Supplier.SupplierID
    GROUP BY Supplier_Item.SKU
) x
WHERE c > 1
ORDER BY c DESC

If this returns just a few rows, then your data is bad. If it returns lots of rows, then your data is not structured the way you think it is. (If it returns zero rows, I'm wrong.)

I'm guessing that you have orders containing the same SKU multiple times (two separate line items, both ordering the same SKU).

egrunin
  • 24,650
  • 8
  • 50
  • 93
6

The select statement in the cost part of your select is returning more than one value. You need to add more where clauses, or use an aggregation.

cjk
  • 45,739
  • 9
  • 81
  • 112
4

The error implies that this subquery is returning more than 1 row:

(Select Supplier_Item.Price from Supplier_Item,orderdetails,Supplier where Supplier_Item.SKU=OrderDetails.Sku and Supplier_Item.SupplierId=Supplier.SupplierID )

You probably don't want to include the orderdetails and supplier tables in the subquery, because you want to reference the values selected from those tables in the outer query. So I think you want the subquery to be simply:

(Select Supplier_Item.Price from Supplier_Item where Supplier_Item.SKU=OrderDetails.Sku and Supplier_Item.SupplierId=Supplier.SupplierID )

I suggest you read up on correlated vs. non-correlated subqueries.

Dave Costa
  • 47,262
  • 8
  • 56
  • 72
3

As others have suggested, the best way to do this is to use a join instead of variable assignment. Re-writing your query to use a join (and using the explicit join syntax instead of the implicit join, which was also suggested--and is the best practice), you would get something like this:

select  
  OrderDetails.Sku,
  OrderDetails.mf_item_number,
  OrderDetails.Qty,
  OrderDetails.Price,
  Supplier.SupplierId, 
  Supplier.SupplierName,
  Supplier.DropShipFees, 
  Supplier_Item.Price as cost
from 
  OrderDetails
join Supplier on OrderDetails.Mfr_ID = Supplier.SupplierId
join Group_Master on Group_Master.Sku = OrderDetails.Sku 
join Supplier_Item on 
  Supplier_Item.SKU=OrderDetails.Sku and Supplier_Item.SupplierId=Supplier.SupplierID 
where 
  invoiceid='339740' 
KP Taylor
  • 2,100
  • 1
  • 17
  • 15
3

Even after 9 years of the original post, this helped me.

Basically your query, SP returning multiple rows.

If you are receiving these types of errors without any clue, It is because of an SP, or check your query. There should be a trigger, a function related to a table, and obviously, it should end up with an SP, or function with selecting/filtering data NOT USING Primary Unique column. If you are searching/filtering using the Primary Unique column there won't be any multiple results. Especially when you are assigning value for a declared variable. The SP never gives you an error but only a runtime error.

 "System.Data.SqlClient.SqlException (0x80131904): Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
    The statement has been terminated."

In my case, there was no clue, only this error message. There was a trigger connected to the table and the table updating by the trigger also had another trigger likewise it ended up with two triggers and in the end with an SP. The SP was having a select clause which was resulting in multiple rows.

SET @Variable1 =(
        SELECT column_gonna_asign
        FROM dbo.your_db
        WHERE Non_primary_non_unique_key= @Variable2

If this returns multiple rows, you are in trouble. Select first

Shammie
  • 131
  • 1
  • 7
0

I got this message in SSMS 19.0.1 when the declaration of a parameter was wrong! Very misleading.

declare @Code varchar = 'N79R-1ZZE-3CW6-C3KM'
  select * from GetSensorDetails( @Code);  // <-- error pointed here.

It reported the error at the line with GetSensorDetails(). The correct declaration requires a length for the varchar:

declare @Code varchar(20) = 'N79R-1ZZE-3CW6-C3KM'  // <-- error actually was here

This fixed it. Hope this helps someone!

Marty
  • 932
  • 6
  • 8