one*_*hen 3 c# sql linq relational-algebra relational-division
关系部门是Codd的原始关系运营商之一,俗称供应所有部件的供应商.已经有各种SQL翻译,例如Celko使用可以飞行机库中所有飞机的飞行员的例子讨论了几种方法.
我更喜欢的是"与集合运算符的划分",因为它是"有余数"(即威尔逊也可以飞行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
);
Run Code Online (Sandbox Code Playgroud)
现在我需要在LINQ to Objects中执行此操作.这是建议的直接翻译:
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 ) );
Run Code Online (Sandbox Code Playgroud)
由于LINQ被认为是基于关系代数,因此直接翻译似乎是合理的.但是有更好的"原生"LINQ方法吗?
反思@Daniel Hilgarth的答案,在.NET Land中,数据可能会被"分组"开头:
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" }, } },
};
Run Code Online (Sandbox Code Playgroud)
...并且仅仅预测名称是任意的,使得潜在的解决方案更加直接:
// 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() );
Run Code Online (Sandbox Code Playgroud)
以下应该产生相同的结果:
pilotSkills.GroupBy(x => x.PilotName, x => x.PlaneName)
.Where(g => hangar.All(y => g.Contains(y.PlaneName)))
Run Code Online (Sandbox Code Playgroud)
这将为每个飞行员返回一组可以飞行机库中所有飞机的组.
该组的关键是飞行员的名字,该组的内容是飞行员可以飞行的所有飞机,甚至是那些不在飞机库中的飞机.
如果您现在只想要飞行员,则可以.Select(g => new { PilotName = g.Key })
在查询结尾添加一个.
使用上述方法Except
,使其更接近OP的原始:
pilotSkills.GroupBy(x => x.PilotName, x => new { x.PlaneName } )
.Where(g => !hangar.Except(g).Any());
Run Code Online (Sandbox Code Playgroud)
第二个查询可能更好,因为它只迭代g
一次; 第一个查询Contains
迭代它N次,其中N是机库中的平面数.
归档时间: |
|
查看次数: |
803 次 |
最近记录: |