什么表现更好:一堆UNION查询与单独的查询?

时间:2014-03-07 13:30:51

标签: sql sql-server union

我有一个自定义商店网站,它使用一组规则(7更精确)来为用户按下购买按钮时找到部件。其中一些规则如下:

  • 寻找与消费者在同一州,市和区的第一个卖家(每个区域只允许一个卖家)
  • 如果上面的卖家没有该部件,请获得与上述相同的卖家并且在同一个城市的卖家
  • 如果上述卖家没有该部件,请联系同一城市的卖家
  • 等......

我已经制定了一系列查询,并且在每个规则的一个巨大联盟中获得了大约300行SQL(格式化以便于阅读),其中一个顶部只有第一个结果。

我不确定这是不是更好的事情,或者最好在序列检查中进行7次有条件的往返,如果后者在调用下一次之前有结果。

我估计大多数用户都将通过规则4,即4次往返SQL Server,只是为了得到一部分,巨型联合查询的结果在不到一秒的小于100,000的表中返回

问题是,如果这个网站开始在可用部分和用户中成长,那么最好让SQL Server处理这个巨大的查询工作,这个查询只返回我在1次旅行中需要的内容,或者更好地调用单独查询?

我们正在使用经典的ASP来完成这项工作(是的!旧学校,你知道吗?),如果这在任何情况下都是相关的。

如果您需要查看查询本身,请告诉我。

修改

刚才结论,这是我现在使用的查询:

SELECT TOP 1 regras.ids, regras.idCadastro, regras.regra, e.descricao AS nomeEstado, cd.descricao AS nomeCidade, b.nome AS nomeBairro
FROM ( 
    SELECT dbo.group_concat(idPecaItem) AS ids, idCadastro, '1' AS regra 
    FROM ( 
        SELECT TOP 2 idPecaItem, idCadastro 
        FROM pecasItens 
        WHERE COALESCE(pecasItens.idCadastro, 0) = (CASE WHEN EXISTS( 
            SELECT TOP 1 COALESCE(pi.idCadastro, 0) 
            FROM pecasItens pi  
            LEFT JOIN cadastros c ON pi.idCadastro = c.idCadastro 
            WHERE (pi.idCadastro IS NULL OR c.tipoParceiro = 'c') AND idPeca = 31740 AND idPedidoItem IS NULL AND dataEntrada <= '2014-3-13' AND devolucaoSolicitada IS NULL
            GROUP BY pi.idCadastro 
            HAVING COUNT(pi.idPecaItem) >= 2
        ) THEN ( 
            SELECT TOP 1 COALESCE(pi.idCadastro, 0) 
            FROM pecasItens pi  
            LEFT JOIN cadastros c ON pi.idCadastro = c.idCadastro 
            WHERE (pi.idCadastro IS NULL OR c.tipoParceiro = 'c') AND idPeca = 31740 AND idPedidoItem IS NULL AND dataEntrada <= '2014-3-13' AND devolucaoSolicitada IS NULL
            GROUP BY pi.idCadastro 
            HAVING COUNT(pi.idPecaItem) >= 2
        ) ELSE NULL END) 
         AND idPeca = 31740 AND idPedidoItem IS NULL AND dataEntrada <= '2014-3-13' AND devolucaoSolicitada IS NULL
    ) regra1 
    GROUP BY idCadastro

    UNION  

    SELECT dbo.group_concat(idPecaItem) AS ids, idCadastro, '2' AS regra 
    FROM ( 
        SELECT TOP 2 idPecaItem, idCadastro 
        FROM pecasItens 
        WHERE COALESCE(pecasItens.idCadastro, 0) = (CASE WHEN EXISTS( 
            SELECT TOP 1 COALESCE(pi.idCadastro, 0) 
            FROM pecasItens pi  
            LEFT JOIN cadastros c ON pi.idCadastro = c.idCadastro AND c.tipoParceiro = 'r' AND c.idCadastroGrupo = 1
            WHERE pi.idCadastro IS NOT NULL  AND idPeca = 31740 AND idPedidoItem IS NULL AND dataEntrada <= '2014-3-13' AND devolucaoSolicitada IS NULL
            GROUP BY pi.idCadastro 
            HAVING COUNT(pi.idPecaItem) >= 2
        ) THEN ( 
            SELECT TOP 1 COALESCE(pi.idCadastro, 0) 
            FROM pecasItens pi  
            LEFT JOIN cadastros c ON pi.idCadastro = c.idCadastro AND c.tipoParceiro = 'r' AND c.idCadastroGrupo = 1
            WHERE pi.idCadastro IS NOT NULL  AND idPeca = 31740 AND idPedidoItem IS NULL AND dataEntrada <= '2014-3-13' AND devolucaoSolicitada IS NULL
            GROUP BY pi.idCadastro 
            HAVING COUNT(pi.idPecaItem) >= 2
        ) ELSE NULL END) 
         AND idPeca = 31740 AND idPedidoItem IS NULL AND dataEntrada <= '2014-3-13' AND devolucaoSolicitada IS NULL
    ) regra2 
    GROUP BY idCadastro 

    UNION 

    SELECT dbo.group_concat(idPecaItem) AS ids, idCadastro, '3' AS regra 
    FROM ( 
        SELECT TOP 2 idPecaItem, idCadastro 
        FROM pecasItens 
        WHERE COALESCE(pecasItens.idCadastro, 0) = (CASE WHEN EXISTS( 
            SELECT TOP 1 COALESCE(pi.idCadastro, 0) 
            FROM pecasItens pi  
            LEFT JOIN cadastros c ON pi.idCadastro = c.idCadastro AND c.tipoParceiro = 'r' AND c.idCadastroGrupo = 1
            INNER JOIN cadastrosCidades cc ON c.idCadastro = cc.idCadastro AND cc.idCidade = 4850
            WHERE pi.idCadastro IS NOT NULL  AND idPeca = 31740 AND idPedidoItem IS NULL AND dataEntrada <= '2014-3-13' AND devolucaoSolicitada IS NULL
            GROUP BY pi.idCadastro 
            HAVING COUNT(pi.idPecaItem) >= 2
        ) THEN ( 
            SELECT TOP 1 COALESCE(pi.idCadastro, 0) 
            FROM pecasItens pi  
            LEFT JOIN cadastros c ON pi.idCadastro = c.idCadastro AND c.tipoParceiro = 'r' AND c.idCadastroGrupo = 1
            INNER JOIN cadastrosCidades cc ON c.idCadastro = cc.idCadastro AND cc.idCidade = 4850
            WHERE pi.idCadastro IS NOT NULL  AND idPeca = 31740 AND idPedidoItem IS NULL AND dataEntrada <= '2014-3-13' AND devolucaoSolicitada IS NULL
            GROUP BY pi.idCadastro 
            HAVING COUNT(pi.idPecaItem) >= 2
        ) ELSE NULL END) 
         AND idPeca = 31740 AND idPedidoItem IS NULL AND dataEntrada <= '2014-3-13' AND devolucaoSolicitada IS NULL
    ) regra3 
    GROUP BY idCadastro 
    UNION  

    SELECT dbo.group_concat(idPecaItem) AS ids, idCadastro, '4' AS regra 
    FROM ( 
        SELECT TOP 2 idPecaItem, idCadastro 
        FROM pecasItens 
        WHERE COALESCE(pecasItens.idCadastro, 0) = (CASE WHEN EXISTS( 
            SELECT TOP 1 COALESCE(pi.idCadastro, 0) 
            FROM pecasItens pi  
            INNER JOIN cadastros c ON pi.idCadastro = c.idCadastro AND c.tipoParceiro = 'r' 
            INNER JOIN cadastrosGrupos cg ON c.idCadastroGrupo = cg.idCadastroGrupo AND cg.idMarca = 2
            INNER JOIN cadastrosCidades cc ON c.idCadastro = cc.idCadastro AND cc.idCidade = 4850
            WHERE pi.idCadastro IS NOT NULL  AND idPeca = 31740 AND idPedidoItem IS NULL AND dataEntrada <= '2014-3-13' AND devolucaoSolicitada IS NULL
            GROUP BY pi.idCadastro 
            HAVING COUNT(pi.idPecaItem) >= 2
        ) THEN ( 
            SELECT TOP 1 COALESCE(pi.idCadastro, 0) 
            FROM pecasItens pi  
            INNER JOIN cadastros c ON pi.idCadastro = c.idCadastro AND c.tipoParceiro = 'r' 
            INNER JOIN cadastrosGrupos cg ON c.idCadastroGrupo = cg.idCadastroGrupo AND cg.idMarca = 2
            INNER JOIN cadastrosCidades cc ON c.idCadastro = cc.idCadastro AND cc.idCidade = 4850
            WHERE pi.idCadastro IS NOT NULL  AND idPeca = 31740 AND idPedidoItem IS NULL AND dataEntrada <= '2014-3-13' AND devolucaoSolicitada IS NULL
            GROUP BY pi.idCadastro 
            HAVING COUNT(pi.idPecaItem) >= 2
        ) ELSE NULL END) 
         AND idPeca = 31740 AND idPedidoItem IS NULL AND dataEntrada <= '2014-3-13' AND devolucaoSolicitada IS NULL
    ) regra4 
    GROUP BY idCadastro 

    UNION  

    SELECT dbo.group_concat(idPecaItem) AS ids, idCadastro, '5' AS regra 
    FROM ( 
        SELECT TOP 2 idPecaItem, idCadastro 
        FROM pecasItens 
        WHERE COALESCE(pecasItens.idCadastro, 0) = (CASE WHEN EXISTS( 
            SELECT TOP 1 COALESCE(pi.idCadastro, 0) 
            FROM pecasItens pi 
            INNER JOIN cadastrosGrupos cg ON pi.idCadastro = cg.idCadastroMontadora AND cg.idCadastroGrupo = 1
            WHERE pi.idCadastro IS NOT NULL  AND idPeca = 31740 AND idPedidoItem IS NULL AND dataEntrada <= '2014-3-13' AND devolucaoSolicitada IS NULL
            GROUP BY pi.idCadastro 
            HAVING COUNT(pi.idPecaItem) >= 2
        ) THEN ( 
            SELECT TOP 1 COALESCE(pi.idCadastro, 0) 
            FROM pecasItens pi 
            INNER JOIN cadastrosGrupos cg ON pi.idCadastro = cg.idCadastroMontadora AND cg.idCadastroGrupo = 1
            WHERE pi.idCadastro IS NOT NULL  AND idPeca = 31740 AND idPedidoItem IS NULL AND dataEntrada <= '2014-3-13' AND devolucaoSolicitada IS NULL
            GROUP BY pi.idCadastro 
            HAVING COUNT(pi.idPecaItem) >= 2
        ) ELSE NULL END) 
         AND idPeca = 31740 AND idPedidoItem IS NULL AND dataEntrada <= '2014-3-13' AND devolucaoSolicitada IS NULL
    ) regra5 
    GROUP BY idCadastro 

    UNION  

    SELECT dbo.group_concat(idPecaItem) AS ids, idCadastro, '6' AS regra 
    FROM ( 
        SELECT TOP 2 idPecaItem, idCadastro 
        FROM pecasItens 
        WHERE COALESCE(pecasItens.idCadastro, 0) = (CASE WHEN EXISTS( 
            SELECT TOP 1 COALESCE(pi.idCadastro, 0) 
            FROM pecasItens pi  
            LEFT JOIN cadastros c ON pi.idCadastro = c.idCadastro AND c.tipoParceiro = 'r' AND c.idEstado = 25 AND c.atendeEstadoTodo = 1 
            WHERE pi.idCadastro IS NOT NULL  AND idPeca = 31740 AND idPedidoItem IS NULL AND dataEntrada <= '2014-3-13' AND devolucaoSolicitada IS NULL
            GROUP BY pi.idCadastro 
            HAVING COUNT(pi.idPecaItem) >= 2
        ) THEN ( 
            SELECT TOP 1 COALESCE(pi.idCadastro, 0) 
            FROM pecasItens pi  
            LEFT JOIN cadastros c ON pi.idCadastro = c.idCadastro AND c.tipoParceiro = 'r' AND c.idEstado = 25 AND c.atendeEstadoTodo = 1 
            WHERE pi.idCadastro IS NOT NULL  AND idPeca = 31740 AND idPedidoItem IS NULL AND dataEntrada <= '2014-3-13' AND devolucaoSolicitada IS NULL
            GROUP BY pi.idCadastro 
            HAVING COUNT(pi.idPecaItem) >= 2
        ) ELSE NULL END) 
         AND idPeca = 31740 AND idPedidoItem IS NULL AND dataEntrada <= '2014-3-13' AND devolucaoSolicitada IS NULL
    ) regra6 
    GROUP BY idCadastro 

    UNION  

    SELECT dbo.group_concat(idPecaItem) AS ids, idCadastro, '7' AS regra 
    FROM ( 
        SELECT TOP 2 idPecaItem, idCadastro 
        FROM pecasItens 
        WHERE COALESCE(pecasItens.idCadastro, 0) = (CASE WHEN EXISTS( 
            SELECT TOP 1 COALESCE(pi.idCadastro, 0) 
            FROM pecasItens pi  
            LEFT JOIN cadastros c ON pi.idCadastro = c.idCadastro AND c.tipoParceiro = 'r' AND c.atendeBrasilTodo = 1 
            WHERE pi.idCadastro IS NOT NULL  AND idPeca = 31740 AND idPedidoItem IS NULL AND dataEntrada <= '2014-3-13' AND devolucaoSolicitada IS NULL
            GROUP BY pi.idCadastro 
            HAVING COUNT(pi.idPecaItem) >= 2
        ) THEN ( 
            SELECT TOP 1 COALESCE(pi.idCadastro, 0) 
            FROM pecasItens pi  
            LEFT JOIN cadastros c ON pi.idCadastro = c.idCadastro AND c.tipoParceiro = 'r' AND c.atendeBrasilTodo = 1 
            WHERE pi.idCadastro IS NOT NULL  AND idPeca = 31740 AND idPedidoItem IS NULL AND dataEntrada <= '2014-3-13' AND devolucaoSolicitada IS NULL
            GROUP BY pi.idCadastro 
            HAVING COUNT(pi.idPecaItem) >= 2
        ) ELSE NULL END) 
         AND idPeca = 31740 AND idPedidoItem IS NULL AND dataEntrada <= '2014-3-13' AND devolucaoSolicitada IS NULL
    ) regra7 
    GROUP BY idCadastro 
) regras 
LEFT JOIN cadastros c ON regras.idCadastro = c.idCadastro 
LEFT JOIN listaEstados e ON c.idEstado = e.idEstado 
LEFT JOIN listaCidades cd ON c.idCidade = cd.idCidade 
LEFT JOIN listaBairros b ON c.idBairro = b.idBairro 
ORDER BY regra ASC

不,这不是MySQL。 group_concat()这是来自这些人的自定义汇总http://groupconcat.codeplex.com/

2 个答案:

答案 0 :(得分:3)

在几乎所有情况下,RDBMS处于更好的位置,可以让你获得第一行。

如果您的查询如下:

SELECT TOP 1 *
FROM (
    SELECT ... FROM ... -- Query #1
UNION ALL
    SELECT ... FROM ... -- Query #2
UNION ALL
    ...
UNION ALL
    SELECT ... FROM ... -- Query #7
)

优化器非常智能,可以根据产生所需结果的规则停止查询,无论是规则1,规则4还是规则7。最重要的是,优化器可以并行运行您的查询。因为它更接近您的数据,所以很难想象您可以通过将部分查询逻辑移离RDBMS来“智取”它。

答案 1 :(得分:1)

我的倾向是将工作留在SQL中,但重组您的查询,使其不是联合。您应该能够提供一个存储过程,内部会在找到结果后立即返回一系列单独的查询。伪sql会像这样开始,并且可能会在此过程中得到改进。

CREATE PROC GetBestSeller (Part, State, City, District) AS BEGIN

  DECLARE @Seller INT

  SELECT TOP 1 @idSeller = idSeller FROM your first choice

  IF @Seller IS NOT NULL BEGIN 
    SELECT * FROM Seller WHERE idSeller = @idSeller
    RETURN
  END

  SELECT TOP 1 @idSeller = idSeller FROM your second choice
    SELECT * FROM Seller WHERE idSeller = @idSeller
    RETURN
  END

..and so one
END
编辑:@Dasblinkenlight可能有权利,因为SQL Server中的查询优化器可能会在内部执行此类操作,您可以查看分析器以查看是否真的发生在UNION上。