Sql-Server
以聲明方式從所有(動態數量)相關實體中選擇一個需要關係的實體
我正在嘗試建構一個 where 子句,該子句需要 X 個子節點與具有 N 對 N 關係的特定父節點建立關係。類似於“獲取這些產品一起銷售的所有收據”。一個範例場景更容易解釋:
DECLARE @Nodes TABLE( Id INT ); DECLARE @Arc TABLE( Id INT IDENTITY(1,1), Source INT, -- FK @Nodes.Id Dest INT -- FK @Nodes.Id ); INSERT INTO @Nodes (Id) VALUES (1),(2),(3),(4); INSERT INTO @Arc (Source, Dest) VALUES (1, 2) ,(1, 3) ,(1, 3) ,(2, 1) ,(2, 3) ,(2, 4);
在 SP 中,輸入被預處理成如下表:
DECLARE @InputConnectedNodes TABLE ( Id INT ); INSERT INTO @InputConnectedNodes (Id) VALUES (3),(4); -- not a fixed number of nodes
在本例中,我們希望所有節點都與節點 3 和 4 有弧(上例中只有節點 2)。目前我們有這個:
SELECT DISTINCT Source FROM @Arc WHERE Dest IN (SELECT Id FROM @InputConnectedNodes);
然後我們有一些醜陋的程式碼循環輸入和(嵌套)輸出檢查所有關係。我想用聲明性的東西代替它。到目前為止,我想出的最好的是:
SELECT Source FROM @Arc WHERE Dest IN (SELECT Id FROM @InputConnectedNodes) GROUP BY Source HAVING COUNT(Source) = (SELECT COUNT(*) FROM @InputConnectedNodes)
但在我的範例中,當節點具有多個(相同的)弧(如 (1->3))時,它將失敗。
我也可以通過動態生成 where 子句來解決這個問題,但我更喜歡完全聲明性的解決方案。
我建議放鬆你對完全聲明性解決方案的決心。可能沒有一種聲明式的方式來解決您的問題,並且在許多情況下,動態 SQL 在性能方面是一個更好的解決方案——即使它缺乏可讀性。
動態 SQL 使優化器有機會使用針對不同參數優化的不同計劃,而不必提出一個可以很好地解決所有排列的計劃。後者很難實現。
您的問題稱為關係除法。查看該標籤中的問題並:
- Divided We Stand: The SQL of Relational Division by Joe Celko;和
- 如何過濾 SQL 會導致 Stack Overflow 上的多次通過關係。
大多數 Stack Overflow 解決方案都可以按原樣工作,或者對 SQL Server 稍作修改。請注意,具有多個連接或子查詢的更複雜的查詢
EXISTS
(對於任意情況需要動態生成的程式碼)比GROUP BY
查詢更有效。