运行速度(低效查询)

时间:2014-04-10 12:51:30

标签: sql performance tsql

我有以下查询,效率不高,很多时候会带回内存消息,有人可以提出任何建议来帮助加快速度吗? 谢谢 吉姆

DECLARE @period_from INT
SET @period_from = 201400

DECLARE @period_to INT
SET @period_to = 201414

Declare @length INT
Set @length = '12'

DECLARE @query VARCHAR(MAX)
SET @query = '%[^-a-zA-Z0-9() ]%'

SELECT 'dim_2' AS field, NULL AS Length, * FROM table1 WHERE client = 'CL'AND period >= @period_from AND @period_to <= @period_to AND dim_2 LIKE @query
UNION
SELECT 'dim_3' AS field, NULL AS Length, * FROM table1 WHERE client = 'CL'AND period >= @period_from AND @period_to <= @period_to AND dim_3 LIKE @query
UNION
SELECT 'dim_4' AS field, NULL AS Length, * FROM table1 WHERE client = 'CL'AND period >= @period_from AND @period_to <= @period_to AND dim_4 LIKE @query
UNION
SELECT 'dim_5' AS field, NULL AS Length, * FROM table1 WHERE client = 'CL'AND period >= @period_from AND @period_to <= @period_to AND dim_5 LIKE @query
UNION
SELECT 'dim_6' AS field, NULL AS Length, * FROM table1 WHERE client = 'CL'AND period >= @period_from AND @period_to <= @period_to AND dim_6 LIKE @query
UNION
SELECT 'dim_7' AS field, NULL AS Length,* FROM table1 WHERE client = 'CL'AND period >= @period_from AND @period_to <= @period_to AND dim_7 LIKE @query
UNION
SELECT 'ext_inv_ref' AS field, NULL AS Length, * FROM table1 WHERE client = 'CL'AND period >= @period_from AND @period_to <= @period_to AND ext_inv_ref LIKE @query
UNION
SELECT 'ext_ref' AS field, NULL AS Length, * FROM table1 WHERE client = 'CL'AND period >= @period_from AND @period_to <= @period_to AND ext_ref LIKE @query
UNION
SELECT 'description' AS field, NULL AS Length, * FROM table1 WHERE client = 'CL'AND period >= @period_from AND @period_to <= @period_to AND description LIKE @query
UNION
SELECT 'Length dim_2' AS field,LEN(dim_2) as Length, * FROM table1 WHERE client = 'CL'AND period >= @period_from AND @period_to <= @period_to AND dim_2 is not null and len(dim_2) >@length
UNION
SELECT 'Length dim_3' AS field, LEN(dim_3) as Length, * FROM table1 WHERE client = 'CL'AND period >= @period_from AND @period_to <= @period_to AND dim_3 is not null and len(dim_3) >@length
UNION
SELECT 'Length dim_4' AS field, LEN(dim_4) as Length, * FROM table1 WHERE client = 'CL'AND period >= @period_from AND @period_to <= @period_to AND dim_4 is not null and len(dim_4) >@length
UNION
SELECT 'Length dim_5' AS field, LEN(dim_5) as Length, * FROM table1 WHERE client = 'CL'AND period >= @period_from AND @period_to <= @period_to AND dim_5 is not null and len(dim_5) >@length
UNION
SELECT 'Length dim_6' AS field, LEN(dim_6) as Length, * FROM table1 WHERE client = 'CL'AND period >= @period_from AND @period_to <= @period_to AND dim_6 is not null and len(dim_6) >@length
UNION
SELECT 'Length dim_7' AS field, LEN(dim_7) as Length, * FROM table1 WHERE client = 'CL'AND period >= @period_from AND @period_to <= @period_to AND dim_7 is not null and len(dim_7) >@length

7 个答案:

答案 0 :(得分:2)

我认为你不能优化这么多。数据库具有dim1到dim7作为一个表的列。现在,您希望将它们视为独立列。因此数据库设计不能满足您的要求。如果这只是一个例外,那么你将不得不忍受它。如果这种用法成为典型访问,那么应该考虑更改数据库设计并为维度添加一个附加表。

您不必要地做的一件事是使用 UNION ,这使得dbms可以查找重复项。由于您的记录以每个联合组的不同常量开始,因此没有。请改用 UNION ALL

答案 1 :(得分:1)

您可以显着减少联合的数量,但是工作会进入WHERE子句。 SQL查询优化器应该知道您只需要为每个union语句遍历表中的行一次,因此它应该更快。试试吧,看看!

SELECT 
CASE 
 WHEN dim_2 like @query Then 'dim_2' 
 WHEN dim_3 like @query Then 'dim_3' 
 WHEN dim_4 like @query Then 'dim_4' 
 WHEN dim_5 like @query Then 'dim_5' 
 WHEN dim_6 like @query Then 'dim_6' 
 WHEN dim_7 like @query Then 'dim_7' 
 WHEN ext_inv_ref LIKE @query Then 'ext_inv_ref'
 WHEN ext_ref LIKE @query Then 'ext_ref'
END AS field, 
NULL AS Length, 
* 
FROM table1 
WHERE client = 'CL'AND period >= @period_from AND @period_to <= @period_to 
AND (dim_2 LIKE @query 
     OR dim_3 LIKE @query 
     OR dim_4 LIKE @query 
     OR dim_5 LIKE @query 
     OR dim_6 LIKE @query 
     OR dim_7 LIKE @query
     OR ext_inv_ref LIKE @query
     OR ext_ref LIKE @query)

UNION

SELECT 
CASE 
 WHEN dim_2 is not null and len(dim_2) >@length Then 'Length dim_2' 
 WHEN dim_3 is not null and len(dim_3) >@length Then 'Length dim_3' 

 ....     

END AS field, 
LEN(dim_2) as Length,
* 
FROM table1 
WHERE client = 'CL'AND period >= @period_from AND @period_to <= @period_to 
AND ((dim_2 is not null and len(dim_2) >@length)
     OR
     (dim_3 is not null and len(dim_3) >@length)
     OR ....
     )

答案 2 :(得分:0)

有许多事情要做,但你可以这样做:

CREATE TEMPORARY TABLE temp_table1 
SELECT * FROM table WHERE client = 'CL' 
AND period >= @period_from AND @period_to <= @period_to; 

然后使用此表作为其他联合的基础,然后删除它。如果经常进行此类选择,则在创建该临时表时,在table1中的字段periodclient上引入复合索引也可能有所帮助:

ALTER TABLE table1 ADD KEY period_client (client, period);

答案 3 :(得分:0)

您可以做的最重要的事情是,如果它在您的权力范围内,就是重新构建数据库,以便将dim字段移动到单独的表中,并且只有一个dim列table(但对于那些有多个dim值的情况,不止一行)。此过程称为数据库规范化。然后,您就可以使用一个SELECT执行查询,而无需UNION一起SELECT个。{/ p>

对于写入的查询(这适用于具有更正的数据库结构的改进查询),您需要确定是否使用索引来解决查询。在您的情况下,最佳索引将是(client, period)(period, client)之一,具体取决于这些索引的基数(基本上,这些列中存在多少个不同的值以及如何均匀地分配这些值。)

如果您已经拥有这些索引中的一个或两个,那么在没有重构数据库的情况下,您无能为力。如果不这样做,您可以尝试依次创建每个索引并查看它是否对查询时间产生影响。

答案 4 :(得分:0)

SELECT T.field
      ,T.Length
      ,table1.* 
FROM table1
     CROSS APPLY (
          VALUES ('dim_2', dim_2, NULL)
                ,('dim_3', dim_3, NULL)
                ,...
                ,('Length dim_2', NULL, LEN(dim_2))
                ,...
     ) AS T(field, value, Length)
WHERE table1.client = 'CL'
      AND table1.period BETWEEN @period_from AND @period_to 
      AND ((T.Length IS NOT NULL AND T.Length > @Length)
           OR (T.value IS NOT NULL AND T.value LIKE @Query))

答案 5 :(得分:0)

您选择结果的方式会耗费大量时间,因为您每次都会查询每次记录所需的数据集,我的解决方案是查询一次并立即获得最终结果:

/* Part 1 : We will define a function that will help us to decompose a value of a column to multiple rows */

ALTER FUNCTION [audit].[FN_Decompose](@p_liste VARCHAR (MAX), @p_separateur CHAR(1))
RETURNS  @table_result TABLE ([id] INT, [indexAt] INT, [element] VARCHAR (255) NULL)
AS
BEGIN

    DECLARE  @v_index       INT
            ,@v_i           INT
            ,@v_longueur    INT
            ,@v_element     VARCHAR(MAX)

    IF ISNULL(@p_liste, '') <> '' 
        BEGIN
            SET @v_index        = 1
            SET @v_i            = 1
            SET @v_longueur     = LEN(@p_liste) + 2
            SET @v_element      = ''

            --pour chaque caractère de la liste
            WHILE @v_index <> @v_longueur 
                BEGIN
                    --si le caractère n''est pas un séparateur
                    IF SUBSTRING(@p_liste, @v_index, 1) <> @p_separateur 
                        BEGIN
                            --Ajouter le caractère à     l''élément courant
                            SET @v_element = @v_element +     SUBSTRING(@p_liste, @v_index,1)
                        END
                    ELSE
                        BEGIN
                            --si l''élément courant n''est     pas vide
                            --IF (@v_element <> '')
                                begin
                                    --Ajout à la table
                                INSERT INTO @table_result ([id],[indexAt],element)
                                VALUES (@v_i, @v_index, @v_element)
                                --Ré-initialisation
                                SET @v_element = ''
                                SET @v_i = @v_i + 1
                            END
                    END

                -- Caractère suivant
                SET @v_index = @v_index + 1
            END
    END

INSERT INTO @table_result VALUES (@v_i, @v_index, @v_element)

RETURN

END

GO


/* THE Script */



DECLARE @period_from INT
SET @period_from = 201400

DECLARE @period_to INT
SET @period_to = 201414

Declare @length INT
Set @length = '12'

DECLARE @query VARCHAR(MAX)
SET @query = '%[^-a-zA-Z0-9() ]%';


WITH Temp AS (
SELECT   
         LEN(dim_2) AS Length_dim2
        ,LEN(dim_3) AS Length_dim3
        ,LEN(dim_4) AS Length_dim4
        ,LEN(dim_5) AS Length_dim5
    ,LEN(dim_6) AS Length_dim6
    ,LEN(dim_7) AS Length_dim7
    ,CASE WHEN dim_1 LIKE @query THEN 'dim_1' ELSE '' END+';'
    +CASE WHEN dim_2 LIKE @query THEN 'dim_2' ELSE '' END+';'
    +CASE WHEN dim_3 LIKE @query THEN 'dim_3' ELSE '' END+';'
    +CASE WHEN dim_4 LIKE @query THEN 'dim_4' ELSE '' END+';'
    +CASE WHEN dim_5 LIKE @query THEN 'dim_5' ELSE '' END+';'
    +CASE WHEN dim_6 LIKE @query THEN 'dim_6' ELSE '' END+';'
    +CASE WHEN dim_7 LIKE @query THEN 'dim_7' ELSE '' END+';'

    +CASE WHEN ext_inv_ref LIKE @query THEN 'ext_inv_ref' ELSE '' END+';'
    +CASE WHEN ext_ref LIKE @query THEN 'ext_ref' ELSE '' END+';'
    +CASE WHEN [description] LIKE @query THEN 'description' ELSE '' END+';'

    +CASE WHEN AND dim_2 is not null and len(dim_2) >@length THEN 'Length dim_2' ELSE '' END+';'
    +CASE WHEN AND dim_3 is not null and len(dim_3) >@length THEN 'Length dim_3' ELSE '' END+';'
    +CASE WHEN AND dim_4 is not null and len(dim_4) >@length THEN 'Length dim_4' ELSE '' END+';'
    +CASE WHEN AND dim_5 is not null and len(dim_5) >@length THEN 'Length dim_5' ELSE '' END+';'
    +CASE WHEN AND dim_6 is not null and len(dim_6) >@length THEN 'Length dim_6' ELSE '' END+';'
    +CASE WHEN AND dim_7 is not null and len(dim_7) >@length THEN 'Length dim_7' ELSE '' END+';' AS fields

FROM table1
WHERE client = 'CL'
AND period >= @period_from
AND @period_to <= @period_to)


SELECT

     CASE fn.element
        WHEN 'Length dim_2' THEN Length_dim2
        WHEN 'Length dim_3' THEN Length_dim3
        WHEN 'Length dim_4' THEN Length_dim4
        WHEN 'Length dim_5' THEN Length_dim5
        WHEN 'Length dim_6' THEN Length_dim6
        WHEN 'Length dim_7' THEN Length_dim7            
     END    AS [Length]
    ,fn.element AS Field
    ,t.* /* The other columns */
FROM Temp t
CROSS APPLY [audit].[FN_Decompose](fields,';') fn
WHERE fn.element IS NOT NULL

祝你好运:)

答案 6 :(得分:0)

只是为了扩展我的评论,如果您有SQLServer 2005或更高版本,您可以取消数据以将列转换为行而不是检查

SELECT id, column_name, value
FROM   (SELECT id, period, dim_2, dim_3, dim_4, dim_5, dim_6
             , dim_7, ext_inv_ref, ext_ref, description 
        FROM myTable) a
       UNPIVOT
       (value FOR column_name 
        IN (dim_2, dim_3, dim_4, dim_5, dim_6
          , dim_7, ext_inv_ref, ext_ref, description)
       ) unpvt
WHERE (value LIKE '%[^-a-zA-Z0-9() ]%' OR len(value) > 12)
  AND period BETWEEN 201400 AND 201414