我应该结合这些查询,如果是,如何?

时间:2014-01-02 19:06:45

标签: sql query-optimization

我有4张桌子:Foo,Bar,Charlie,&三角洲。桌子查理,三角洲和& Bar都有一个包含Foo主键的外键列。外键列具有唯一约束,因此每个键列中只能有一个链接到给定的Foo。此外,由于域名的建模方式,不应该将Charlie和Delta连接到同一个Foo。

我有一个Bar,我想知道是否有一个Charlie或Delta链接到相关的Foo,如果没有,那么我需要来自相关Foo的其他数据。

目前我这样做是以1-3个查询:

  1. 获取与链接到条形图的Foo链接的Charlie的主键(如果有)
  2. 如果没有Charlie,请获取链接到条形码的Foo链接的主键,如果有的话
  3. 如果没有Delta,请从Foo链接到Bar
  4. 获取其他一些列

    由于历史原因,我们不使用存储过程;每个查询都是在代码中构建的(我们使用C#,如果由于某种原因它是相关的),并且在可能运行下一个查询之前检查其结果。

    对于Oracle&对于MS SQL Server 2008(我们支持两者),单独运行这3个查询或以某种方式将它们组合成单个查询是否更快?如果将它们组合起来更快,我该怎么做?

    编辑:将每个表视为具有两列。 Foo有列PRIMARY_KEY和OTHER_DATA,而其他三个表有PRIMARY_KEY和FOO列,其中FOO是包含Foo中一行主键的外键。

    3个查询基本上如下所示:

    1. SELECT C.PRIMARY_KEY FROM Bar B, Charlie C WHERE B.FOO = C.FOO(返回0或1行)
    2. SELECT D.PRIMARY_KEY FROM Bar B, Delta D WHERE B.FOO = D.FOO(返回0或1行)
    3. SELECT F.OTHER_DATA FROM Bar B, Foo F WHERE B.FOO = F.PRIMARY_KEY(返回1行)

3 个答案:

答案 0 :(得分:3)

是的,它可能可能更快地组合它们。 这是因为你有机会避免多次回到Bar。

当然,答案很长,它始终取决于您的索引和硬件设置(以及其他所有内容:))。 因此,您必须以任何新方式实际测试旧方法,并寻求显着改进。

由于你说所有这些都是1-1(或0-1)关系,我所看到的是你真正为每个Foo记录创建一个扩展记录。

没有什么能阻止你写作

select
    foo.*       -- of course, specific columns is better
    ,bar.*      -- of course, specific columns is better
    ,c.*        -- of course, specific columns is better
    ,d.*        -- of course, specific columns is better

from
    foo
        inner join bar on foo.pk = bar.fooId
            left join charlie c on bar.fooId = c.fooId
            left join delta d on bar.fooId = c.fooId

我知道在这种情况下,SQL Server只能连接到Bar一次,从而节省了处理和可能的磁盘I / O.
而且因为你使用相同的连接键,所以这让我更加自信,因为不存在为不同连接重新排序数据的问题。
数据库“引擎”应该能够很好地管理它们。

多次查询应该是一个性能错误,因为Bar会一次又一次地被读取。

在这样一个基本操作中,很可能同样的论点适用于Oracle,但我不是那里的专家。

答案 1 :(得分:0)

我同意Mike M.每次往返数据库都会显着降低你的app和DB的速度 话虽如此,你可以只对你的数据库进行一次往返,虽然结果集可能会有点大,当你把3个表的结果串起来时(只是说)。 只要表Charlie,Delta和Foo是对称的(具有相同的列),您就可以使用UNION语句来组合结果。喜欢这个

SELECT 'Charlie' AS SourceTable, C.PRIMARY_KEY 
FROM Bar B INNER JOIN Charlie C ON B.FOO = C.FOO
UNION
SELECT 'Delta' AS SourceTable, D.PRIMARY_KEY 
FROM Bar B INNER JOIN Delta D ON B.FOO = D.FOO
UNION
SELECT 'Foo' AS SourceTable, F.OTHER_DATA 
FROM Bar B INNER JOIN Foo F ON B.FOO = F.PRIMARY_KEY

否则,Mike M的答案(使用LEFT JOINs)也是一个非常好的选择,只要你在其他3个表中没有重复的.foo ID(这会产生笛卡尔产品=邪恶)。您可以通过向Mike M的答案添加更多WHERE子句来过滤掉其他表中的行来避免这些。

答案 2 :(得分:0)

由于您使用C#进行编码,因此可以使用Linq to SQL:

var query = from f in Foo
            // inner equijoin:
            join b in Bar on f.PRIMARY_KEY equals b.FOO
            // left outer join:
            join tc in Charlie on f.PRIMARY_KEY equals tc.FOO into gc
            from c in gc.DefaultIfEmpty()
            // left outer join:
            join td in Delta on f.PRIMARY_KEY equals td.FOO into gd
            from d in gd.DefaultIfEmpty()
            // anonymous object for result set:
            select new {
                Key = f.PRIMARY_KEY,
                Data = f.OTHER_DATA,
                HasCharlie = c == null,
                HasDelta = d == null
            };

                      // get first row with charlie or delta
var resultRow = query.FirstOrDefault(row => row.HasCharlie || row.HasDelta);

if (resultRow == null)
{
                               // get first row, regardless of charlie or delta
    var otherResultRow = query.FirstOrDefault();
}

构建query也可以通过一系列方法调用来完成:Foo.Join(Bar,...).GroupJoin(Charlie,...).SelectMany(...)等。

如果要“烘焙”查询结果,可以使用Linq to Objects将query.ToList()分配给变量,并对该变量执行将来的操作,而无需再次访问数据库。或者,如果您在FirstOrDefault上使用了以后的操作(例如我上面代码中的query),它们将反映在执行代码期间对数据库所做的任何更改。