У меня есть хранимая процедура и функция на SQL Server. Но всякий раз, когда я запускаю его, я получаю такую ошибку
Подзапрос вернул более 1 значения. Это не разрешено, когда подзапрос следует за =, !=, <, <= , >, >= или когда подзапрос используется как выражение.
Это моя функция CekStokTersedia (для проверки наличия):
USE [Hotel]
GO
/****** Object: UserDefinedFunction [dbo].[CekStokTersedia] Script Date: 03/04/2023 12:20:08 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[CekStokTersedia] (@tglCheckin datetime, @tglCheckout datetime, @qty int, @kodeMenu varchar(100))
RETURNS bit
AS
BEGIN
DECLARE @stok float
DECLARE @results TABLE (STOK bit)
INSERT INTO @results (STOK)
SELECT CASE WHEN ISNULL(Stock_Akhir, 0) >= @qty THEN 1 ELSE 0 END AS STOK
FROM Tr_Type_S
WHERE T_Type = @kodeMenu AND tanggal >= @tglCheckin AND tanggal <= @tglCheckout
DECLARE @result bit
SELECT @result = STOK FROM @results WHERE STOK = 0
-- tambahkan kondisi pengecekan apakah ada data dengan range tanggal yang dimasukkan
IF NOT EXISTS (SELECT 1 FROM Tr_Type_S WHERE T_Type = @kodeMenu AND tanggal >= @tglCheckin AND tanggal <= @tglCheckout)
SET @result = 0
RETURN @result
END
Тогда это моя хранимая процедура CheckStockMenu:
USE [Hotel]
GO
/****** Object: StoredProcedure [dbo].[CheckStockMenu] Script Date: 03/04/2023 12:20:29 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--EXEC [dbo].[CheckStockMenu] '2023-04-01', '2023-04-03', 'PA03,PA02', '2,10'
ALTER PROCEDURE [dbo].[CheckStockMenu]
@Checkin datetime,
@Checkout datetime,
@MenuKode varchar(100),
@qty varchar(100)
AS
BEGIN
SET NOCOUNT ON;
DECLARE @stokTersedia bit = 1;
DECLARE @menuKodes TABLE (kode varchar(100), qty int);
DECLARE @menuKodeTidakCukup varchar(100) = '';
DECLARE @qtyInt AS int = (SELECT CAST(value AS int) FROM STRING_SPLIT(@qty, ','));
INSERT INTO @menuKodes (kode, qty)
SELECT kode.value, CAST(qty.value AS int)
FROM STRING_SPLIT(@MenuKode, ',') AS kode
JOIN STRING_SPLIT(@qty, ',') AS qty
ON kode.[key] = qty.[key]
DECLARE @kodesMenu varchar(100);
DECLARE menu_cursor CURSOR FOR SELECT kode FROM @menuKodes;
OPEN menu_cursor;
FETCH NEXT FROM menu_cursor INTO @kodesMenu;
WHILE @@FETCH_STATUS = 0
BEGIN
-- Pengecekan apakah data dengan range antara @Checkin dan @Checkout ada atau tidak
IF (SELECT COUNT(tanggal) FROM Tr_Type_S WHERE T_Type = @kodesMenu AND tanggal >= @Checkin AND tanggal <= @Checkout) <> DATEDIFF(day, @Checkin, @Checkout) + 1
BEGIN
SET @stokTersedia = 0;
SET @menuKodeTidakCukup = CONCAT(@menuKodeTidakCukup, @kodesMenu, ', ');
END
-- Pengecekan stok pada setiap menu yang dimasukkan
ELSE IF dbo.CekStokTersedia(@Checkin, @Checkout, @qtyInt, @kodesMenu) = 0
BEGIN
SET @stokTersedia = 0;
SET @menuKodeTidakCukup = CONCAT(@menuKodeTidakCukup, @kodesMenu, ', ');
END
FETCH NEXT FROM menu_cursor INTO @kodesMenu;
END
CLOSE menu_cursor;
DEALLOCATE menu_cursor;
IF @stokTersedia = 1
SELECT 'Stok tersedia' AS Status;
ELSE
SELECT CONCAT('Stok tidak cukup untuk menu dengan kode ', LEFT(@menuKodeTidakCukup, LEN(@menuKodeTidakCukup) - 1)) AS Status;
END
Затем я запускаю процедуру следующим образом:
EXEC [dbo].[CheckStockMenu] '2023-04-01', '2023-04-03', 'PA03,PA02', '2,10'
И есть такая ошибка:
Подзапрос вернул более 1 значения. Это не разрешено, когда подзапрос следует за =, !=, <, <= , >, >= или когда подзапрос используется как выражение.
Пожалуйста, помогите мне решить эту проблему...
Вы передаете значение '2,10'
вместо @qty
(т.е. два значения), но попробуйте присвоить это одному значению int здесь:
DECLARE @qtyInt AS int = (SELECT CAST(value AS int) FROM STRING_SPLIT(@qty, ','));
Каких @qtyInt
вы ожидаете здесь, 2 или 10, и почему? Вам нужно дать SQL Server правильную логику, чтобы определить, какое значение выбрать, поскольку @qtyInt
может содержать только одно значение.
Хотя, просмотрев вашу процедуру, я не думаю, что вам вообще нужно это назначение, ваш курсор уже включает это количество, поэтому вы можете удалить проблемную строку и просто назначить это в своем выборке курсора:
FETCH NEXT FROM menu_cursor INTO @kodesMenu, @qtyInt;
Наконец, хотя я не знаком со всей вашей бизнес-логикой, я был бы очень удивлен, если бы это нельзя было переписать намного более эффективно, используя подход, основанный на наборах. Подобные процедурные подходы обычно плохо масштабируются.
я думаю, что подзапрос возвращает более одного значения, но код пытается сравнить его с одним значением (проблема связана с функцией dbo.CekStokTersedia
, которая возвращает более одного значения в определенных сценариях)
причиной ошибки могло быть использование параметра @qtyInt
в dbo.CekStokTersedia
@qtyInt
— это одно целое значение — запрос внутри функции ожидает сравнения его с несколькими значениями из Stock_Akhir (может привести к тому, что подзапрос вернет несколько значений)
INSERT INTO @results (STOK)
SELECT CASE WHEN ISNULL(ts.Stock_Akhir, 0) >= @qty THEN 1 ELSE 0 END AS STOK
FROM @tglCheckin AS ci
INNER JOIN @tglCheckout AS co ON ci.idx = co.idx
INNER JOIN Tr_Type_S ts ON ts.tanggal >= ci.tgl AND ts.tanggal <= co.tgl AND ts.T_Type = @kodeMenu
Tr_Type_S
с двумя временными таблицами @tglCheckin
и @tglCheckout
, чтобы получить список дат между датами заезда и выездаStock_Akhir
для каждой даты@results
(должно вернуть одно значение)PS. вы можете пересмотреть использование курсоров в хранимой процедуре (они могут быть проблемой производительности и иногда могут приводить к неожиданным результатам) - лучше использовать операции на основе набора вместо курсоров - особенно для больших наборов данных
спасибо за ваше предложение, я изменил запрос, который у меня есть, используя запрос на соединение, и он работает, хотя и с несколькими другими изменениями.
Ошибка говорит сама за себя, отладьте свой процесс, найдите запрос, который ее вызывает, и исправьте логику. Кому-то другому очень сложно сделать это за вас без доступа к минимально воспроизводимому примеру