Підзапит SQL Server повернув більше 1 значення. Це заборонено, коли підзапит слідує =,! =, <, <=,>,> =


84

Я запускаю такий запит:

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  

Я отримую таку помилку:

Повідомлення 512, рівень 16, стан 1, рядок 2 Підзапит повернув більше 1 значення. Це заборонено, коли підзапит слідує =,! =, <, <=,>,> = Або коли підзапит використовується як вираз.

Будь-які ідеї?


40
О, і припиніть використовувати синтаксис мається на увазі, це дуже погана практика, її важче підтримувати та легше робити помилки.
HLGEM,

1
@HLGEM, чому це погана практика, її важче підтримувати та легше робити помилки?
реггігітара

5
На яких полях об’єднані ці таблиці? Підказка: проблема в тому, що я не можу сказати.
naughtilus

Відповіді:


49

Спробуйте це:

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'

Це поверне кілька ідентичних рядків, крім costстовпця. Подивіться на різні значення вартості, які повертаються, і з’ясуйте, що спричиняє різні значення. Потім запитайте когось, яку вартість собівартості вони хочуть, і додайте критерії до запиту, який вибере цю вартість.


44

Перевірте, чи в таблиці є тригери, щодо яких ви намагаєтесь виконати запити. Вони іноді можуть викинути цю помилку, намагаючись запустити тригер оновлення / вибору / вставки, який знаходиться в таблиці.

Ви можете змінити свій запит на вимкнення, а потім увімкнути тригер, якщо тригер НЕ потрібно виконувати для будь-якого запиту, який ви намагаєтеся виконати.

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]

4
Чи не краще було б виправити тригер (и), а не вимикати їх? Ці тригери були створені з причини, ні? Ви можете обійти деякі важливі функції, вимкнувши тригер (и) ...
TT.

3
@TT. Так, але, будь ласка, див. Жирний текст у відповіді. Ви можете змінити свій запит, щоб вимкнути, а потім увімкнути тригер, якщо тригер НЕ потрібно виконувати для будь-якого запиту, який ви намагаєтеся виконати.
jk.

1
Зміна таблиці під час запиту абсолютно жахлива. Якщо вам потрібно пропустити тригер, робіть це для кожного підключення.
Бен Войгт

26
SELECT COLUMN 
    FROM TABLE 
WHERE columns_name
    IN ( SELECT COLUMN FROM TABLE WHERE columns_name = 'value');

примітка: коли ми використовуємо підзапит, ми повинні зосередитись на таких моментах:

  1. якщо наш підзапит повертає 1 значення, у цьому випадку нам потрібно використовувати (=,! =, <>, <,> ....)
  2. else (більше одного значення), у цьому випадку нам потрібно використовувати (в, будь-якому, усьому, в деяких)

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

Цей підзапит повертає кілька значень, SQL скаржиться, оскільки він не може призначити кілька значень, які коштують в одному записі.

Деякі ідеї:

  1. Виправте дані таким чином, щоб існуючий підзапит повертав лише 1 запис
  2. Виправте підзапит таким чином, щоб він повертав лише один запис
  3. Додайте верхню 1 і впорядкуйте підзапит (неприємне рішення, яке ненавидять адміністратори баз даних, але воно "працює")
  4. Використовуйте визначену користувачем функцію для об’єднання результатів підзапиту в один рядок

5
На 3; ВСІ компетентні розробники також повинні це ненавидіти. Нещодавно було запитання про "Pet Peeves"; і моє буде: "Те, що немає повідомлень про помилки, не означає, що воно" працює "!". Тим не менш, ви можете додати №5: Реструктурувати весь запит; тобто замість того, щоб отримувати рахунки клієнтів та "шукати"; скоріше отримуйте рахунки-фактури та шукайте клієнтів.
Розчарований

10

Або ваші дані погані, або вони структуровані не так, як ви думаєте. Можливо, і те, і інше.

Щоб довести / спростувати цю гіпотезу, запустіть цей запит:

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

Якщо це повертає лише кілька рядків, то ваші дані погані . Якщо він повертає багато рядків, то ваші дані не структуровані так, як ви думаєте. (Якщо він повертає нуль рядків, я помиляюся. )

Я здогадуюсь, що у вас є замовлення, що містять однакові SKUкілька разів (дві окремі позиції, обидва замовляють однаково SKU).


10

Виправлення полягає в тому, щоб припинити використання корельованих підзапитів і замість цього використовувати об’єднання. Відповідні підзапити по суті є курсорами, оскільки вони змушують виконувати запит підряд, і їх слід уникати.

Вам може знадобитися похідна таблиця в об'єднанні, щоб отримати потрібне значення в полі, якщо ви хочете, щоб збігався лише один запис, якщо вам потрібні обидва значення, тоді це joinзробить звичайний , але ви отримаєте кілька записів для одного ідентифікатора у наборі результатів. Якщо ви хочете лише один, вам потрібно вирішити, який саме, і зробити це в коді, ви можете використовувати a top 1з order by, ви могли б використовувати max(), ви могли б використовувати min()тощо, залежно від того, яка ваша реальна потреба в даних.


9

У мене була та сама проблема, яку я використав inзамість =, з Northwindприкладу бази даних:

Запит: Знайдіть компанії, які розміщували замовлення в 1997 році

Спробуйте це :

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

Замість цього:

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

6

Оператор select у частині витрат вашого select повертає більше одного значення. Вам потрібно додати більше пропозицій where, або використовувати агрегацію.


4

Помилка означає, що цей підзапит повертає більше 1 рядка:

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

Ймовірно, ви не хочете включати деталі замовлення та таблиці постачальників у підзапит, оскільки ви хочете посилатися на значення, вибрані з цих таблиць у зовнішньому запиті. Тому я думаю, що ви хочете, щоб підзапит був просто:

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

Я пропоную вам прочитати про корельовані та некорельовані підзапити.


3

Як припускали інші, найкращий спосіб зробити це - використовувати об'єднання замість присвоєння змінної. Переписавши свій запит на використання об’єднання (і використовуючи явний синтаксис об’єднання замість неявного об’єднання, що також було запропоновано - і є найкращою практикою), ви отримаєте щось подібне:

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' 

1

Навіть через 9 років оригінальної публікації це мені допомогло.

Якщо ви отримуєте такі типи помилок без будь-якого підказки, повинен бути тригер, функція, пов’язана з таблицею, і, очевидно, вона повинна закінчуватися SP, або функція з вибором / фільтруванням даних, НЕ ВИКОРИСТАННЯ первинного унікального стовпця. Якщо ви шукаєте / фільтруєте за допомогою первинного унікального стовпця, результатів не буде багато. Особливо, коли ви призначаєте значення для оголошеної змінної. SP ніколи не видає помилок en, а лише помилку виконання.

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

У моєму випадку, очевидно, не було ніякої підказки, а лише це повідомлення про помилку. До таблиці був підключений тригер, і оновлення таблиці за допомогою тригера також мало інший тригер, так само він закінчився двома тригерами і, зрештою, SP. SP мав пропозицію select, результатом якої було кілька рядків.

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

Якщо це повертає кілька рядків, у вас трапляються проблеми.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.