Member 9017207 Ответов: 2

Получить строки в SQL до тех пор, пока сумма столбца (кол-во покупки) не превысит кол-во закрытия запаса


У меня два стола. Таблица покупок и таблица закрытия запасов.

СТОЛ КУПИТЬ

Стол купить

И ЗАКРЫТИЕ БИРЖЕВОЙ ТАБЛИЦЫ

Запас в таблице

Я хочу вернуть все строки из таблицы покупок на основе Transaction_Date desc до тех пор, пока сумма(Purchase_qty) не превысит Closing_Stock из таблицы CLOSING STOCK TABLE.

Предположим, что для Material_Code AB01 в таблице покупок closing_stock равен 42 в таблице закрытия таблицы запасов. В этом случае возвращаемые строки будут такими, как показано ниже.

Выход

Как вы можете видеть, сумма (Purchase_qty) первых 4 записей равна 43, поэтому она превышает 42. Я не хочу показывать последнюю колонку. Таким образом, логика будет заключаться в том, что когда сумма(Purchase_qty) превысит closing_stock, остановитесь там.

Что я уже пробовал:

Я пробовал ниже запрос, но это не дает результата. Любой вклад очень важен.

SELECT A.Material_Code,A.Transaction_Date, A.Purchase_qty, 
sum(A.Purchase_qty) OVER (ORDER BY Material_Code) AS total 
from Purchase A 
left join Closing_Stock B ON
A.Material_Code = B.Material_Code
where A.Purchase_qty <= B.Closing_Stock
order by A.Transaction_Date desc

2 Ответов

Рейтинг:
1

David_Wimbley

Спасибо за предоставление примеров данных и схемы, это было действительно полезно.

Я предоставил 2 возможных запроса, чтобы получить то, что вам нужно. Вариант а-это обычный запрос group by, а вариант в - обычное табличное выражение.

Я смоделировал вашу схему с помощью временных таблиц, вы должны быть в состоянии заменить ее #PurchaseTable и другая временная таблица с именами ваших таблиц.

Не уверен, что что-то из этого нуждается в большом объяснении, но вариант а делает фильтрацию того, является ли общее количество больше, чем закрытие запаса в HAVING пункт.

Вариант B использует общее табличное выражение (Использование Общих Табличных Выражений[^]) и обрабатывает фильтрацию данных как подзапрос в предложении where

IF OBJECT_ID('tempdb..#PurchaseTable') IS NOT NULL DROP TABLE #PurchaseTable
CREATE TABLE #PurchaseTable
(
	Id INT NULL,
	Material_Code VARCHAR(25) NULL,
	Batch VARCHAR(25) NULL,
	Purchase_qty INT NULL,
	Transaction_num VARCHAR(25) NULL,
	Transaction_Date DATETIME NULL
)

IF OBJECT_ID('tempdb..#ClosingStock') IS NOT NULL DROP TABLE #ClosingStock
CREATE TABLE #ClosingStock
(
	Id INT NULL,
	Material_Code VARCHAR(25) NULL,
	Batch VARCHAR(25) NULL,
	Closing_Stock INT NULL
)


INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 1 , 'AB01' ,'E01' ,42 )
INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 3 , 'AB02' ,'E02' ,77)
INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 4 , 'AB03' ,'E03' ,44 )
INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 5 , 'AB04' ,'E05' ,55 )
INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 6 , 'AB05' ,'E05' ,142 )
INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 7 , 'AB06' ,'E05' ,98 )

INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (1,'AB01','E01', 22, 'GR1' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (2,'AB01','E01', 12, 'GR2' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (3,'AB01','E01', 9, 'GR3' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (4,'AB01','E01', 11, 'GR4' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (5,'AB05','E05', 11, 'GR5' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (6,'AB05','E05', 22, 'GR6' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (7,'AB05','E05', 44, 'GR7' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (8,'AB05','E05', 29, 'GR8' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (9,'AB05','E05', 33, 'GR9' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (10,'AB05','E05', 34, 'GR10' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (11,'AB03','E03', 34, 'GR11' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (12,'AB03','E03', 6, 'GR12' ,'2017-08-28')



--Option A
SELECT 
	A.Material_Code, 
	A.Batch, 
	SUM(A.Purchase_qty) AS Total 
FROM #PurchaseTable AS A
GROUP BY A.Material_Code,  A.Batch
HAVING SUM(A.Purchase_qty) > (SELECT B.Closing_Stock FROM #ClosingStock AS B WHERE B.Material_Code = A.Material_Code AND B.Batch = A.Batch)


GO
--Option B
WITH StockCte (Material_Code, Batch, Total)
AS
(
	SELECT 
		A.Material_Code, 
		A.Batch, 
		SUM(A.Purchase_qty) AS Total 
	FROM #PurchaseTable AS A
	GROUP BY A.Material_Code,  A.Batch
)
SELECT * FROM StockCte AS A WHERE A.Total > (SELECT B.Closing_Stock FROM #ClosingStock AS B WHERE B.Material_Code = A.Material_Code AND B.Batch = A.Batch)


РЕДАКТИРОВАТЬ:

Обновлено на основе комментария OP

--Stores all possible batch keys
DECLARE @BatchKeys TABLE (
	Id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
	MaterialCode varchar(25) NULL,
	Batch varchar(25) NULL
);

--Temp storage of purchase id's by @BatchKeys, gets cleared after each batch key loop
DECLARE @PurchaseIds TABLE (
	Id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
	PurchaseId INT NULL
)

--Stores all acceptable purchase id's below threshold
DECLARE @FinalPurchaseIds TABLE (
	Id INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
	PurchaseId INT NULL
);

INSERT INTO @BatchKeys( MaterialCode, Batch )
SELECT Material_Code, Batch FROM #ClosingStock

DECLARE @BatchId INT = (SELECT MIN(Id) FROM @BatchKeys)

WHILE @BatchId IS NOT NULL
BEGIN
	--//BEGIN Loop of batch keys
	DECLARE @MaterialCode VARCHAR(25) = (SELECT MaterialCode FROM @BatchKeys WHERE Id = @BatchId);
	DECLARE @Batch VARCHAR(25) = (SELECT Batch FROM @BatchKeys WHERE Id = @BatchId);
	
	DECLARE @ClosingTotal INT = (SELECT A.Closing_Stock FROM #ClosingStock AS A WHERE A.Material_Code = @MaterialCode AND A.Batch = @Batch)

	-- Add all purchase id's to table for analyzing
	INSERT INTO @PurchaseIds ( PurchaseId )	
		SELECT Id FROM #PurchaseTable AS A WHERE A.Material_Code = @MaterialCode AND A.Batch = @Batch
	
	DECLARE @PurchaseId INT = (SELECT MIN(Id) FROM @PurchaseIds)
	DECLARE @TotalPurchaseAmount INT = 0;

	WHILE @PurchaseId IS NOT NULL
	BEGIN
		PRINT 'Material Code: ' + @MaterialCode + ' Batch Code: ' + @Batch + ' - Total Purchase Qty ' + CAST(@TotalPurchaseAmount AS VARCHAR(20));
		--//BEGIN Loop of purchase id's 
		DECLARE @TargetPurchaseId INT = (SELECT A.PurchaseId FROM @PurchaseIds AS A WHERE A.Id = @PurchaseId)
		DECLARE @PurchaseAmount INT = (SELECT A.Purchase_qty FROM #PurchaseTable AS A WHERE A.Id = @TargetPurchaseId)

		-- Decide here if we are over the limit
		IF @PurchaseAmount + @TotalPurchaseAmount > @ClosingTotal
		BEGIN
			BREAK;
		END

		SET @TotalPurchaseAmount = @PurchaseAmount + @TotalPurchaseAmount;

		-- THis is just so we can keep track of what Id's are within the threshold
		INSERT INTO @FinalPurchaseIds (PurchaseId )
		SELECT @TargetPurchaseId
		
		
		--//END Loop of purchase id's
		SELECT @PurchaseId = MIN(Id) FROM @PurchaseIds WHERE Id > @PurchaseId
	END
        -- This table is temp storage of purchase id's by material/batch. So clear it out for the next loop so we don't total up qty's from other material/batch codes.
	DELETE FROM @PurchaseIds
	--//END Loop of batch keys
    SELECT @BatchId = MIN(Id) FROM @BatchKeys WHERE Id > @BatchId
END;

--Lists all purchase records below Closing_Stock
SELECT A.*, B.Closing_Stock FROM #PurchaseTable AS A
JOIN #ClosingStock AS B ON B.Material_Code = A.Material_Code AND A.Batch = B.Batch 
WHERE A.Id IN (SELECT PurchaseId FROM @FinalPurchaseIds)

--Validation query to show total qty with CLosing Stock count
SELECT A.Material_Code, A.Batch, SUM(A.Purchase_qty), B.Closing_Stock FROM #PurchaseTable AS A
JOIN #ClosingStock AS B ON B.Material_Code = A.Material_Code AND A.Batch = B.Batch 
WHERE A.Id IN (SELECT PurchaseId FROM @FinalPurchaseIds)
GROUP BY A.Material_Code, A.Batch, B.Closing_Stock


Member 9017207

Это нечто совершенно иное, чем я ожидал в результате. Я хотел показать все строки до тех пор, пока сумма(purchase_qty) не превысит сумму закрытия акции. Даже показать следующий ряд. Но это все.

David_Wimbley

Извините, что я не прочитал Ваш вопрос внимательно. Я обновил свой ответ, хотя лично я думаю, что вам нужно изменить свою схему.

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

Мое предложение состояло бы в том, чтобы изменить вашу схему для хранения текущей суммы по MaterialCode и Batch, будь то в новой таблице или в таблице покупок, и тогда вы могли бы просто сказать что-то вроде SELECT * FROM PurchasesTable AS A WHERE A.TotalQty < (SELECT Closing_Qty FROM ClosingQty AS B WHERE B.MaterialCode = A.MaterialCode AND B.Batch = A.BAtch) Таким образом, запрос будет выполняться намного быстрее, не придется анализировать ваши данные строка за строкой и, надеюсь, не станет узким местом в вашем приложении.

Просто предложение, вы, конечно, можете делать то, что считаете нужным, но вышеприведенная правка в моем ответе должна дать, если я правильно понял вас на этот раз, то, что вам нужно

Рейтинг:
0

Bryian Tan

Мне пришлось позаимствовать код для создания временных таблиц из Дэвид [^]:) Приведенный ниже запрос суммирует количество покупки, а затем отфильтровывает запись, которая превысила заключительный запас. Согласно вашему примеру на AB01, я не вижу простого способа отобразить 4 строки и скрыть пятую, потому что 43 > 42. возможно, кто-то может помочь вам добавить в нее дополнительную логику.

IF OBJECT_ID('tempdb..#PurchaseTable') IS NOT NULL DROP TABLE #PurchaseTable
CREATE TABLE #PurchaseTable
(
	Id INT NULL,
	Material_Code VARCHAR(25) NULL,
	Batch VARCHAR(25) NULL,
	Purchase_qty INT NULL,
	Transaction_num VARCHAR(25) NULL,
	Transaction_Date DATETIME NULL
)
 
IF OBJECT_ID('tempdb..#ClosingStock') IS NOT NULL DROP TABLE #ClosingStock
CREATE TABLE #ClosingStock
(
	Id INT NULL,
	Material_Code VARCHAR(25) NULL,
	Batch VARCHAR(25) NULL,
	Closing_Stock INT NULL
)
 

INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 1 , 'AB01' ,'E01' ,42 )
INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 3 , 'AB02' ,'E02' ,77)
INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 4 , 'AB03' ,'E03' ,44 )
INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 5 , 'AB04' ,'E05' ,55 )
INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 6 , 'AB05' ,'E05' ,142 )
INSERT INTO #ClosingStock ( Id ,Material_Code ,Batch ,Closing_Stock) VALUES  ( 7 , 'AB06' ,'E05' ,98 )
 
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (1,'AB01','E01', 22, 'GR1' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (2,'AB01','E01', 17, 'GR2' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (3,'AB01','E01', 9, 'GR3' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (4,'AB01','E01', 11, 'GR4' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (5,'AB05','E05', 11, 'GR5' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (6,'AB05','E05', 22, 'GR6' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (7,'AB05','E05', 44, 'GR7' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (8,'AB05','E05', 29, 'GR8' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (9,'AB05','E05', 33, 'GR9' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (10,'AB05','E05', 34, 'GR10' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (11,'AB03','E03', 34, 'GR11' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (12,'AB03','E03', 6, 'GR12' ,'2017-08-28')
INSERT INTO #PurchaseTable ( Id ,Material_Code ,Batch ,Purchase_qty ,Transaction_num ,Transaction_Date) VALUES  (13,'AB01','E01', 6, 'GR13' ,'2017-08-28')

;WITH tempSUM AS (
select 
    id, Material_Code, Batch, Purchase_qty, Transaction_num,Transaction_Date,
    sum(Purchase_qty) over (partition by Material_Code order by Material_Code, id DESC) running_total
from #PurchaseTable
)
SELECT s.*, Closing_Stock FROM tempSUM s JOIN
#ClosingStock c ON s.Material_Code=c.Material_Code AND s.Batch = c.Batch
WHERE Closing_Stock > running_total


Выход:
[Выход]
id	Material_Code	Batch	Purchase_qty	Transaction_num	Transaction_Date	running_total	Closing_Stock
13	AB01	E01	6	GR13	2017-08-28 00:00:00.000	6	42
4	AB01	E01	11	GR4	2017-08-28 00:00:00.000	17	42
3	AB01	E01	9	GR3	2017-08-28 00:00:00.000	26	42
12	AB03	E03	6	GR12	2017-08-28 00:00:00.000	6	44
11	AB03	E03	34	GR11	2017-08-28 00:00:00.000	40	44
10	AB05	E05	34	GR10	2017-08-28 00:00:00.000	34	142
9	AB05	E05	33	GR9	2017-08-28 00:00:00.000	67	142
8	AB05	E05	29	GR8	2017-08-28 00:00:00.000	96	142
7	AB05	E05	44	GR7	2017-08-28 00:00:00.000	140	142


Ресурсы:
в SQL - нарастающий итог по сгруппированным записям в таблице - переполнение стека [^]


Karthik_Mahalingam

5

Bryian Tan

Спасибо, сэр!!!