哪个SQL查询更快?过滤加入条件或Where子句?

Jon*_*son 83 sql t-sql sql-server sql-server-2008

比较这两个查询.将过滤器放在连接条件或are子句中是否更快.我一直认为它在连接标准上更快,因为它在最快的时刻减少了结果集,但我不确定.

我将构建一些测试来查看,但我也希望得到关于哪个更清晰易读的意见.

查询1

SELECT      *
FROM        TableA a
INNER JOIN  TableXRef x
        ON  a.ID = x.TableAID
INNER JOIN  TableB b
        ON  x.TableBID = b.ID
WHERE       a.ID = 1            /* <-- Filter here? */
Run Code Online (Sandbox Code Playgroud)

查询2

SELECT      *
FROM        TableA a
INNER JOIN  TableXRef x
        ON  a.ID = x.TableAID
        AND a.ID = 1            /* <-- Or filter here? */
INNER JOIN  TableB b
        ON  x.TableBID = b.ID
Run Code Online (Sandbox Code Playgroud)

编辑

我运行了一些测试,结果显示它实际上非常接近,但该WHERE条款实际上稍快一点!=)

我绝对同意在WHERE条款上应用过滤器更有意义,我只是对性能影响感到好奇.

已确认的时间标准: 143016 ms
已安排时间加入标准: 143256 ms

测试

SET NOCOUNT ON;

DECLARE @num    INT,
        @iter   INT

SELECT  @num    = 1000, -- Number of records in TableA and TableB, the cross table is populated with a CROSS JOIN from A to B
        @iter   = 1000  -- Number of select iterations to perform

DECLARE @a TABLE (
        id INT
)

DECLARE @b TABLE (
        id INT
)

DECLARE @x TABLE (
        aid INT,
        bid INT
)

DECLARE @num_curr INT
SELECT  @num_curr = 1

WHILE (@num_curr <= @num)
BEGIN
    INSERT @a (id) SELECT @num_curr
    INSERT @b (id) SELECT @num_curr

    SELECT @num_curr = @num_curr + 1
END

INSERT      @x (aid, bid)
SELECT      a.id,
            b.id
FROM        @a a
CROSS JOIN  @b b

/*
    TEST
*/
DECLARE @begin_where    DATETIME,
        @end_where      DATETIME,
        @count_where    INT,
        @begin_join     DATETIME,
        @end_join       DATETIME,
        @count_join     INT,
        @curr           INT,
        @aid            INT

DECLARE @temp TABLE (
        curr    INT,
        aid     INT,
        bid     INT
)

DELETE FROM @temp

SELECT  @curr   = 0,
        @aid    = 50

SELECT  @begin_where = CURRENT_TIMESTAMP
WHILE (@curr < @iter)
BEGIN
    INSERT      @temp (curr, aid, bid)
    SELECT      @curr,
                aid,
                bid
    FROM        @a a
    INNER JOIN  @x x
            ON  a.id = x.aid
    INNER JOIN  @b b
            ON  x.bid = b.id
    WHERE       a.id = @aid

    SELECT @curr = @curr + 1
END
SELECT  @end_where = CURRENT_TIMESTAMP

SELECT  @count_where = COUNT(1) FROM @temp
DELETE FROM @temp

SELECT  @curr = 0
SELECT  @begin_join = CURRENT_TIMESTAMP
WHILE (@curr < @iter)
BEGIN
    INSERT      @temp (curr, aid, bid)
    SELECT      @curr,
                aid,
                bid
    FROM        @a a
    INNER JOIN  @x x
            ON  a.id = x.aid
            AND a.id = @aid
    INNER JOIN  @b b
            ON  x.bid = b.id

    SELECT @curr = @curr + 1
END
SELECT  @end_join = CURRENT_TIMESTAMP

SELECT  @count_join = COUNT(1) FROM @temp
DELETE FROM @temp

SELECT  @count_where AS count_where,
        @count_join AS count_join,
        DATEDIFF(millisecond, @begin_where, @end_where) AS elapsed_where,
        DATEDIFF(millisecond, @begin_join, @end_join) AS elapsed_join
Run Code Online (Sandbox Code Playgroud)

Qua*_*noi 58

在性能方面,它们是相同的(并产生相同的计划)

从逻辑上讲,如果替换INNER JOIN为a ,则应该使操作仍然有意义LEFT JOIN.

在你的情况下,这将是这样的:

SELECT  *
FROM    TableA a
LEFT JOIN
        TableXRef x
ON      x.TableAID = a.ID
        AND a.ID = 1
LEFT JOIN
        TableB b
ON      x.TableBID = b.ID
Run Code Online (Sandbox Code Playgroud)

或这个:

SELECT  *
FROM    TableA a
LEFT JOIN
        TableXRef x
ON      x.TableAID = a.ID
LEFT JOIN
        TableB b
ON      b.id = x.TableBID
WHERE   a.id = 1
Run Code Online (Sandbox Code Playgroud)

前一个查询不会返回a.id除了之外的任何实际匹配1,因此后一种语法(with WHERE)在逻辑上更加一致.


Rem*_*anu 19

对于内部联接,您在何处放置标准并不重要.SQL编译器将两者转换为执行计划,其中过滤发生在连接下方(即,好像过滤器表达式出现在连接条件中).

外连接是另一回事,因为过滤器的位置改变了查询的语义.

  • 使用外部联接的@Ananth会为JOIN条件不匹配的联接表的所有列提供NULL。过滤器将不满足NULL并消除行,从而将有效的OUTER连接变为INNER连接。 (2认同)

Rob*_*Day 8

就这两种方法而言.

  • JOIN/ON用于连接表格
  • 在哪里过滤结果

虽然你可以用不同的方式使用它们,但对我来说它似乎总是一种气味.

在出现问题时处理性能问题.然后你可以研究这样的"优化".

  • 这似乎并不能回答问题。 (3认同)