SQL Server Subquery zwróciło więcej niż 1 wartość. Nie jest to dozwolone, gdy podzapytanie następuje po =,! =, <, <=,>,> =

84

Uruchamiam następujące zapytanie:

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  

Otrzymuję następujący błąd:

Msg 512, poziom 16, stan 1, wiersz 2 podzapytanie zwróciło więcej niż 1 wartość. Nie jest to dozwolone, gdy podzapytanie następuje po =,! =, <, <=,>,> = Lub gdy podzapytanie jest używane jako wyrażenie.

Jakieś pomysły?

Anilkumar
źródło
40
Aha i przestań używać implikowanej składni złączeń. Jest to bardzo słaba praktyka, trudniejsza do utrzymania i łatwiejsza do popełnienia błędów.
HLGEM
1
@HLGEM dlaczego jest to słaba praktyka, trudniejsza w utrzymaniu i łatwiejsza z błędami?
reggaeguitar
5
Na jakich polach są połączone te tabele? Wskazówka: problem stanowi fakt, że nie potrafię powiedzieć.
naughtilus

Odpowiedzi:

49

Spróbuj tego:

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'

Spowoduje to zwrócenie wielu identycznych wierszy z wyjątkiem costkolumny. Przyjrzyj się różnym wartościom kosztu, które są zwracane i dowiedz się, co powoduje różne wartości. Następnie zapytaj kogoś, jaką wartość kosztu chce, i dodaj kryteria do zapytania, które wybierze ten koszt.

Jeffrey L Whitledge
źródło
44

Sprawdź, czy w tabeli są jakieś wyzwalacze, dla których próbujesz wykonać zapytania. Czasami mogą zgłaszać ten błąd, gdy próbują uruchomić wyzwalacz aktualizacji / wyboru / wstawiania znajdujący się w tabeli.

Możesz zmodyfikować zapytanie, aby wyłączyć, a następnie włączyć wyzwalacz, jeśli wyzwalacz NIE musi być wykonywany dla żadnego zapytania, które próbujesz uruchomić.

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]
jk.
źródło
4
Czy nie byłoby lepiej naprawić wyzwalacze niż je wyłączać? Te wyzwalacze zostały stworzone z jakiegoś powodu, prawda? Możesz ominąć niektóre ważne funkcje, wyłączając wyzwalacze ...
TT.
3
@TT. Tak, ale proszę zapoznać się z pogrubionym tekstem w odpowiedzi. Możesz zmodyfikować zapytanie, aby wyłączyć, a następnie włączyć wyzwalacz, jeśli wyzwalacz NIE musi być wykonywany dla żadnego zapytania, które próbujesz uruchomić.
jk.
1
Zmiana tabeli podczas zapytania jest absolutnie straszna. Jeśli chcesz pominąć wyzwalacz, zrób to dla każdego połączenia.
Ben Voigt,
26
SELECT COLUMN 
    FROM TABLE 
WHERE columns_name
    IN ( SELECT COLUMN FROM TABLE WHERE columns_name = 'value');

uwaga: kiedy używamy zapytania podrzędnego, musimy skupić się na następujących punktach:

  1. jeśli nasze zapytanie podrzędne zwraca 1 wartość, w tym przypadku musimy użyć (=,! =, <>, <,> ....)
  2. else (więcej niż jedna wartość), w tym przypadku musimy użyć (in, any, all, some)
Diyar sereroy
źródło
13
cost = Select Supplier_Item.Price from Supplier_Item,orderdetails,Supplier 
   where Supplier_Item.SKU=OrderDetails.Sku and 
      Supplier_Item.SupplierId=Supplier.SupplierID

To podzapytanie zwraca wiele wartości, a SQL narzeka, ponieważ nie może przypisać wielu wartości do kosztu w jednym rekordzie.

Jakieś pomysły:

  1. Popraw dane w taki sposób, aby istniejące podzapytanie zwracało tylko 1 rekord
  2. Napraw podzapytanie tak, aby zwracało tylko jeden rekord
  3. Dodaj pierwsze 1 i sortuj według do podzapytania (paskudne rozwiązanie, którego administratorzy nienawidzą - ale „działa”)
  4. Użyj funkcji zdefiniowanej przez użytkownika, aby połączyć wyniki podzapytania w jeden ciąg
Mayo
źródło
5
Na 3; WSZYSCY kompetentni programiści również powinni tego nienawidzić. Jakiś czas temu było pytanie dotyczące „Pet Peeves”; a moje brzmiałoby: „Tylko dlatego, że nie ma komunikatów o błędach, nie oznacza, że ​​'działa'!”. To powiedziawszy, możesz dodać # 5: Przebuduj całe zapytanie; tzn. zamiast pobierać faktury od klientów i „sprawdzać” faktury; raczej otrzymuj faktury i „szukaj” klientów.
Rozczarowany
10

Albo Twoje dane są złe, albo nie mają takiej struktury, jaką myślisz. Prawdopodobnie jedno i drugie.

Aby udowodnić / obalić tę hipotezę, uruchom następujące zapytanie:

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

Jeśli to zwróci tylko kilka wierszy, Twoje dane są złe . Jeśli zwraca wiele wierszy, oznacza to, że dane nie są ustrukturyzowane tak, jak myślisz. (Jeśli zwraca zero wierszy, to się mylę. ).

Zgaduję, że masz zamówienia zawierające to samo SKUwiele razy (dwa oddzielne elementy zamówienia, które składają takie samo zamówienie SKU).

egrunin
źródło
10

Rozwiązaniem jest zaprzestanie używania skorelowanych podzapytań i zamiast tego używanie złączeń. Skorelowane podzapytania są zasadniczo kursorami, ponieważ powodują uruchamianie zapytania wiersz po wierszu i należy ich unikać.

Możesz potrzebować tabeli pochodnej w sprzężeniu, aby uzyskać żądaną wartość w polu, jeśli chcesz, aby pasował tylko jeden rekord, jeśli potrzebujesz obu wartości join, zrobi to zwykły , ale otrzymasz wiele rekordów dla tego samego identyfikatora w zestawie wyników. Jeśli chcesz tylko jednego, musisz zdecydować, który z nich i zrobić to w kodzie, możesz użyć a top 1z order by, możesz użyć max(), możesz użyć min()itp., W zależności od tego, jakie są twoje rzeczywiste wymagania dotyczące danych.

HLGEM
źródło
9

Miałem ten sam problem, którego użyłem inzamiast =, z Northwindprzykładu bazy danych:

Zapytanie brzmi: Znajdź firmy, które złożyły zamówienia w 1997 roku

Spróbuj tego :

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

Zamiast tego :

SELECT CompanyName
    FROM Customers
WHERE CustomerID =
(
    SELECT CustomerID 
        FROM Orders 
    WHERE YEAR(OrderDate) = '1997'
);
JAN
źródło
6

Instrukcja select w wybranej części dotyczącej kosztów zwraca więcej niż jedną wartość. Musisz dodać więcej klauzul Where lub użyć agregacji.

cjk
źródło
4

Błąd oznacza, że ​​to podzapytanie zwraca więcej niż 1 wiersz:

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

Prawdopodobnie nie chcesz uwzględniać szczegółów zamówienia i tabel dostawców w podzapytaniu, ponieważ chcesz odwołać się do wartości wybranych z tych tabel w zewnętrznym zapytaniu. Więc myślę, że chcesz, aby podzapytanie wyglądało po prostu:

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

Proponuję zapoznać się z podzapytaniami skorelowanymi i nieskorelowanymi.

Dave Costa
źródło
3

Jak sugerowali inni, najlepszym sposobem na to jest użycie sprzężenia zamiast przypisania zmiennej. Ponownie pisząc zapytanie, aby użyć złączenia (i używając jawnej składni złączenia zamiast niejawnego złączenia, co również zostało zasugerowane - i jest najlepszą praktyką), otrzymamy coś takiego:

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
źródło
1

Pomogło mi to nawet po 9 latach od oryginalnego postu.

Jeśli otrzymujesz tego typu błędy bez żadnej wskazówki, powinien istnieć wyzwalacz, funkcja związana z tabelą i oczywiście powinna kończyć się SP lub funkcją z wybieraniem / filtrowaniem danych NIE UŻYWAJĄC kolumny Primary Unique. Jeśli wyszukujesz / filtrujesz za pomocą kolumny Primary Unique, nie będzie wielu wyników. Zwłaszcza, gdy przypisujesz wartość zadeklarowanej zmiennej. SP nigdy nie podaje błędu en, a jedynie błąd wykonania.

 "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."

W moim przypadku nie było oczywiście żadnej wskazówki, a jedynie ten komunikat błędu. Był wyzwalacz podłączony do tabeli, a aktualizacja tabeli przez wyzwalacz miała również inny wyzwalacz, podobnie zakończyło się dwoma wyzwalaczami, a na końcu SP. SP miał klauzulę select, która powodowała tworzenie wielu wierszy.

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

Jeśli to zwróci wiele wierszy, masz kłopoty.

Shammie
źródło