關係部門是Codd的原始關係運算符之一,通俗地說,它是供應所有零件的供應商。已經有各種翻譯成SQL的例子Celko使用the pilots who can fly all the planes in the hangar的例子討論了幾種方法。LINQ中的關係部門?
我更喜歡的是「與集合操作員的分工」,因爲它是「剩餘的」(即威爾遜也可以飛F-17戰鬥機,但機庫中沒有一個)以及它如何處理案件除數是空集(即當機庫是空的,那麼所有的飛行員返回):
WITH PilotSkills
AS
(
SELECT *
FROM (
VALUES ('Celko', 'Piper Cub'),
('Higgins', 'B-52 Bomber'), ('Higgins', 'F-14 Fighter'),
('Higgins', 'Piper Cub'),
('Jones', 'B-52 Bomber'), ('Jones', 'F-14 Fighter'),
('Smith', 'B-1 Bomber'), ('Smith', 'B-52 Bomber'),
('Smith', 'F-14 Fighter'),
('Wilson', 'B-1 Bomber'), ('Wilson', 'B-52 Bomber'),
('Wilson', 'F-14 Fighter'), ('Wilson', 'F-17 Fighter')
) AS T (pilot_name, plane_name)
),
Hangar
AS
(
SELECT *
FROM (
VALUES ('B-1 Bomber'),
('B-52 Bomber'),
('F-14 Fighter')
) AS T (plane_name)
)
SELECT DISTINCT pilot_name
FROM PilotSkills AS P1
WHERE NOT EXISTS (
SELECT plane_name
FROM Hangar
EXCEPT
SELECT plane_name
FROM PilotSkills AS P2
WHERE P1.pilot_name = P2.pilot_name
);
現在我需要在LINQ這樣做是爲了對象。這裏有一個建議直接翻譯:
var hangar = new []
{
new { PlaneName = "B-1 Bomber" },
new { PlaneName = "F-14 Fighter" },
new { PlaneName = "B-52 Bomber" }
}.AsEnumerable();
var pilotSkills = new []
{
new { PilotName = "Celko", PlaneName = "Piper Cub" },
new { PilotName = "Higgins", PlaneName = "B-52 Bomber" },
new { PilotName = "Higgins", PlaneName = "F-14 Fighter" },
new { PilotName = "Higgins", PlaneName = "Piper Cub" },
new { PilotName = "Jones", PlaneName = "B-52 Bomber" },
new { PilotName = "Jones", PlaneName = "F-14 Fighter" },
new { PilotName = "Smith", PlaneName = "B-1 Bomber" },
new { PilotName = "Smith", PlaneName = "B-52 Bomber" },
new { PilotName = "Smith", PlaneName = "F-14 Fighter" },
new { PilotName = "Wilson", PlaneName = "B-1 Bomber" },
new { PilotName = "Wilson", PlaneName = "B-52 Bomber" },
new { PilotName = "Wilson", PlaneName = "F-14 Fighter" },
new { PilotName = "Wilson", PlaneName = "F-17 Fighter" }
}.AsEnumerable();
var actual = pilotSkills.Where
(
p1 => hangar.Except
(
pilotSkills.Where(p2 => p2.PilotName == p1.PilotName)
.Select(p2 => new { p2.PlaneName })
).Any() == false
).Select(p1 => new { p1.PilotName }).Distinct();
var expected = new []
{
new { PilotName = "Smith" },
new { PilotName = "Wilson" }
};
Assert.That(actual, Is.EquivalentTo(expected));
由於LINQ is supposedly based on the relational algebra然後直接翻譯似乎是合理的。但是有沒有更好的「原生」LINQ方法?
反思@Daniel Hilgarth的回答,在.NET土地的數據很可能是「組合」以開始:
var pilotSkills = new []
{
new { PilotName = "Celko",
Planes = new []
{ new { PlaneName = "Piper Cub" }, } },
new { PilotName = "Higgins",
Planes = new []
{ new { PlaneName = "B-52 Bomber" },
new { PlaneName = "F-14 Fighter" },
new { PlaneName = "Piper Cub" }, } },
new { PilotName = "Jones",
Planes = new []
{ new { PlaneName = "B-52 Bomber" },
new { PlaneName = "F-14 Fighter" }, } },
new { PilotName = "Smith",
Planes = new []
{ new { PlaneName = "B-1 Bomber" },
new { PlaneName = "B-52 Bomber" },
new { PlaneName = "F-14 Fighter" }, } },
new { PilotName = "Wilson",
Planes = new []
{ new { PlaneName = "B-1 Bomber" },
new { PlaneName = "B-52 Bomber" },
new { PlaneName = "F-14 Fighter" },
new { PlaneName = "F-17 Fighter" }, } },
};
...和預測只是名稱是任意的,使得可能的解決方案要簡單得多:
// Easy to understand at a glance:
var actual1 = pilotSkills.Where(x => hangar.All(y => x.Planes.Contains(y)));
// Potentially more efficient:
var actual = pilotSkills.Where(x => !hangar.Except(x.Planes).Any());
你不需要'AsEnumerable()',數組的IEnumerable –
2013-04-25 08:21:37
Codd的劃分並不是原始的。非正式地,它返回「供應所有部件的供應商」和*供應至少一部分的供應商。 (雖然我同意你的非正式措辭可悲是常見的措辭),所以這實際上並不是那麼有用。 「方法」不是原文的「翻譯」,它們是原始*和不同但令人想起*的操作符的翻譯。 (關於關係子集運算符的最佳/最簡單的推理,然後轉換爲代數/演算。)PS「LINQ被認爲是基於關係代數」 - 哈哈。受到啓發,OK。 – philipxy 2017-12-14 09:04:44
@philipxy引用來自我引用的文章:「本文將單子和LINQ描述爲關係代數的泛化」。如果你有反駁的引用,我會非常感興趣。同樣,我不是故意暗示分裂是原始的,也不認爲我有;我所鏈接的文章指的是(有點非正式的)託德和羅姆利的。但是如果你有什麼需要了解爲什麼師不是一個原始的操作員,那麼它會受到感謝。 – onedaywhen 2018-01-09 08:32:36