chức năng treo với hoạt động trường hợp null


9

Tôi đã tạo một hàm chấp nhận ngày bắt đầu và ngày kết thúc, với ngày kết thúc là tùy chọn. Sau đó tôi đã viết một CASEtrong bộ lọc để sử dụng ngày bắt đầu nếu không có ngày kết thúc được thông qua.

CASE WHEN @dateEnd IS NULL
    THEN @dateStart
    ELSE @dateEnd
END

Khi tôi gọi hàm cho tháng gần nhất của dữ liệu:

SELECT * FROM theFunction ('2013-06-01', NULL)

... truy vấn bị treo. Nếu tôi chỉ định ngày kết thúc:

SELECT * FROM theFunction ('2013-06-01', '2013-06-01')

... kết quả được trả lại bình thường. Tôi lấy mã ra khỏi hàm và chạy nó tốt trong một cửa sổ truy vấn. Tôi cũng không thể nhân đôi vấn đề. Một truy vấn như:

SELECT * FROM theFunction ('2013-04-01', '2013-06-01')

... cũng hoạt động tốt.

Có bất cứ điều gì trong truy vấn (bên dưới) có thể khiến hàm bị treo khi a NULLđược thông qua cho ngày kết thúc không?

Câu đố SQL


Bạn có thể đăng thêm logic? Những gì bạn có không nên gây ra vấn đề.
Kenneth Fisher

3
Nếu bạn thay thế CASEbằng COALESCE(@dateEnd,@dateStart), vấn đề vẫn xuất hiện?
ypercubeᵀᴹ

2
Và với ISNULL()?
ypercubeᵀᴹ

3
Có phải là bận rộn hoặc chờ đợi trên một cái gì đó? Trong khi nó được "treo" thì nó SELECT task_state FROM sys.dm_os_tasks WHERE session_id = x thể hiện điều gì ? Nếu nó dành nhiều thời gian không ở RUNNINGtrạng thái thì kiểu chờ đợi đó là phiên sys.dm_os_waiting_tasksnào?
Martin Smith

1
@ypercube Không cải thiện với COALESCE. ISNULLĐã sửa nó.
Kermit

Câu trả lời:


7

Một phần của truy vấn ban đầu của bạn là như sau.

  FROM   [dbo].[calendar] a
          LEFT JOIN [dbo].[colleagueList] b
            ON b.[Date] = a.d
   WHERE  DAY(a.[d]) = 1
          AND a.[d] BETWEEN @dateStart AND COALESCE(@dateEnd,@dateStart) 

Phần đó của kế hoạch được hiển thị dưới đây

nhập mô tả hình ảnh ở đây

Truy vấn sửa đổi của bạn BETWEEN @dateStart AND ISNULL(@dateEnd,@dateStart)có điều này cho cùng tham gia

nhập mô tả hình ảnh ở đây

Sự khác biệt dường như là ISNULLđơn giản hóa hơn nữa và kết quả là bạn có được số liệu thống kê chính xác hơn về việc tham gia tiếp theo. Đây là một hàm có giá trị bảng nội tuyến và bạn đang gọi nó với các giá trị bằng chữ để nó có thể làm một cái gì đó như thế.

 a.[d] BETWEEN @dateStart AND ISNULL(@dateEnd,@dateStart) 
 a.[d] BETWEEN '2013-06-01' AND ISNULL(NULL,'2013-06-01') 
 a.[d] BETWEEN '2013-06-01' AND '2013-06-01'
 a.[d] = '2013-06-01'

Và vì có một vị từ tham gia đẳng thức b.[Date] = a.d, kế hoạch cũng cho thấy một vị từ đẳng thức b.[Date] = '2013-06-01'. Do đó, ước tính cardinality của 28,393các hàng có thể khá chính xác.

Đối với phiên bản CASE/ COALESCEkhi @dateStart@dateEndcó cùng giá trị thì nó đơn giản hóa OK cho cùng một biểu thức đẳng thức và đưa ra cùng một kế hoạch nhưng khi @dateStart = '2013-06-01'@dateEnd IS NULLnó chỉ đi xa đến mức

a.[d]>='2013-06-01' AND a.[Date]<=CASE WHEN (1) THEN '2013-06-01' ELSE NULL END

mà nó cũng được áp dụng như là một vị ngữ ngụ ý trên ColleagueList. Số lượng hàng ước tính lần này là 79.8hàng.

Sự tham gia tiếp theo cùng là

   LEFT JOIN colleagueTime
     ON colleagueTime.TC_DATE = colleagueList.Date
        AND colleagueTime.ASSOC_ID = CAST(colleagueList.ID AS VARCHAR(10)) 

colleagueTimelà một 3,249,590bảng hàng (một lần nữa) rõ ràng là một đống không có chỉ mục hữu ích.

Sự khác biệt trong ước tính này ảnh hưởng đến sự lựa chọn tham gia được sử dụng. Các ISNULLkế hoạch chọn một băm tham gia mà chỉ quét bảng một lần. Các COALESCEkế hoạch chọn một vòng lặp lồng nhau tham gia và ước tính rằng nó sẽ vẫn chỉ cần quét các bảng một lần và có thể ống chỉ kết quả và phát lại nó 78 lần. tức là nó ước tính rằng các tham số tương quan sẽ không thay đổi.

Từ thực tế là kế hoạch các vòng lặp lồng nhau vẫn đang diễn ra sau hai giờ, giả định này về một lần quét chống lại colleagueTimedường như rất không chính xác.

Về lý do tại sao số lượng hàng ước tính giữa hai liên kết thấp hơn rất nhiều, tôi không chắc chắn nếu không thể xem số liệu thống kê trên các bảng. Cách duy nhất tôi quản lý để làm lệch số lượng hàng ước tính mà phần lớn trong thử nghiệm của tôi là thêm một tải NULLhàng (điều này làm giảm số lượng hàng ước tính mặc dù số lượng hàng thực tế được trả lại vẫn giữ nguyên).

Số lượng hàng ước tính trong COALESCEkế hoạch với dữ liệu thử nghiệm của tôi là theo thứ tự

number of rows matching >= condition * 30% * (proportion of rows in the table not null)

Hoặc trong SQL

SELECT 1E0 * COUNT([Date]) / COUNT(*) * ( COUNT(CASE
                                                  WHEN [Date] >= '2013-06-01' THEN 1
                                                END) * 0.30 )
FROM   [dbo].[colleagueList] 

nhưng điều này không vuông với nhận xét của bạn rằng cột không có NULLgiá trị.


"bạn có tỷ lệ giá trị NULL rất cao trong cột Ngày trong bảng đó không?" Tôi không có NULLgiá trị cho ngày trong bất kỳ bảng nào.
Kermit

@FreshPrinceOfSO - Thật đáng tiếc. Tôi vẫn không biết tại sao lại có sự khác biệt lớn như vậy trong hai ước tính sau đó. Trong các thử nghiệm, tôi đã thực hiện bộ lọc bitmap và vị từ bổ sung dường như không làm thay đổi các ước tính về số lượng thẻ có thể thực hiện ở đây.
Martin Smith

@FreshPrinceOfSO - Mặc dù nếu bạn cảm thấy muốn viết kịch bản thống kê, tôi có thể thử và tìm ra nó.
Martin Smith

Tôi đang trên 2008R2; khi tôi chọn Chọn lược đồ , dbokhông được liệt kê. Chỉ là các lược đồ khác mà tôi không sử dụng.
Kermit

4

Dường như có vấn đề với các loại dữ liệu. ISNULLđã khắc phục sự cố (cảm ơn ypercube ). Sau một số nghiên cứu, COALESCEtương đương với CASEtuyên bố mà tôi đang sử dụng:

CASE
   WHEN (expression1 IS NOT NULL) THEN expression1
   WHEN (expression2 IS NOT NULL) THEN expression2
   ...
   ELSE expressionN
END

Paul White giải thích rằng:

COALESCE( expression [ ,...n ] ) trả về kiểu dữ liệu của biểu thức có quyền ưu tiên kiểu dữ liệu cao nhất.

ISNULL(check_expression, replacement_value) trả về cùng loại với check_expression.

Để tránh mọi vấn đề về kiểu dữ liệu, có vẻ như đây ISNULLlà hàm thích hợp để sử dụng để chỉ xử lý hai biểu thức.

Trích đoạn kế hoạch XML

Kế hoạch XML sử dụng CASE, biểu thức 2 là NULL:

SELECT * FROM theFunction ('2013-06-01', NULL)
<ScalarOperator ScalarString="CASE WHEN (1) THEN '2013-06-01' ELSE NULL END">
  <IF>
    <Condition>
      <ScalarOperator>
        <Const ConstValue="(1)"/>
      </ScalarOperator>
    </Condition>
    <Then>
      <ScalarOperator>
        <Const ConstValue="'2013-06-01'"/>
      </ScalarOperator>
    </Then>
    <Else>
      <ScalarOperator>
        <Const ConstValue="NULL"/>
      </ScalarOperator>
    </Else>
  </IF>
</ScalarOperator>

Kế hoạch XML sử dụng CASE, biểu thức 2 là một ngày:

SELECT * FROM theFunction ('2013-06-01', '2013-06-01')
<ScalarOperator ScalarString="CASE WHEN [Expr1035]=(0) THEN NULL ELSE [Expr1036] END">
  <IF>
    <Condition>
      <ScalarOperator>
        <Compare CompareOp="EQ">
          <ScalarOperator>
            <Identifier>
              <ColumnReference Column="Expr1035"/>
            </Identifier>
          </ScalarOperator>
          <ScalarOperator>
            <Const ConstValue="(0)"/>
          </ScalarOperator>
        </Compare>
      </ScalarOperator>
    </Condition>
    <Then>
      <ScalarOperator>
        <Const ConstValue="NULL"/>
      </ScalarOperator>
      </Then>
    <Else>
      <ScalarOperator>
        <Identifier>
          <ColumnReference Column="Expr1036"/>
        </Identifier>
      </ScalarOperator>
    </Else>
  </IF>
</ScalarOperator>

Kế hoạch XML sử dụng ISNULL, biểu thức 2 là NULL:

SELECT * FROM theFunction ('2013-06-01', NULL)
<ScalarOperator ScalarString="CASE WHEN [Expr1035]=(0) THEN NULL ELSE [Expr1036] END">
  <IF>
    <Condition>
      <ScalarOperator>
        <Compare CompareOp="EQ">
          <ScalarOperator>
            <Identifier>
              <ColumnReference Column="Expr1035"/>
            </Identifier>
          </ScalarOperator>
          <ScalarOperator>
            <Const ConstValue="(0)"/>
          </ScalarOperator>
        </Compare>
      </ScalarOperator>
    </Condition>
    <Then>
      <ScalarOperator>
        <Const ConstValue="NULL"/>
      </ScalarOperator>
    </Then>
    <Else>
      <ScalarOperator>
        <Identifier>
          <ColumnReference Column="Expr1036"/>
        </Identifier>
      </ScalarOperator>
    </Else>
  </IF>
</ScalarOperator>

Kế hoạch XML sử dụng ISNULL, biểu thức 2 là một ngày:

SELECT * FROM theFunction ('2013-06-01', '2013-06-01')
<ScalarOperator ScalarString="CASE WHEN [Expr1035]=(0) THEN NULL ELSE [Expr1036] END">
  <IF>
    <Condition>
      <ScalarOperator>
        <Compare CompareOp="EQ">
          <ScalarOperator>
            <Identifier>
              <ColumnReference Column="Expr1035"/>
            </Identifier>
          </ScalarOperator>
          <ScalarOperator>
            <Const ConstValue="(0)"/>
          </ScalarOperator>
        </Compare>
      </ScalarOperator>
    </Condition>
    <Then>
      <ScalarOperator>
        <Const ConstValue="NULL"/>
      </ScalarOperator>
    </Then>
    <Else>
      <ScalarOperator>
        <Identifier>
          <ColumnReference Column="Expr1036"/>
        </Identifier>
      </ScalarOperator>
    </Else>
  </IF>
</ScalarOperator>

Nhưng điều đó không giải thích tại sao nó hoạt động tốt SELECT * FROM theFunction ('2013-06-01', '2013-06-01'). Kiểu dữ liệu biểu thức vẫn giống nhau. Và cả hai tham số là datekiểu dữ liệu nào. Bạn có thể xem các kế hoạch thực hiện?
Martin Smith

@MartinSmith Đây là kế hoạch cho truy vấn trả về kết quả. Tôi không có kế hoạch khi biểu thức thứ hai là NULL.
Kermit

Truyền các biểu thức bên trong CASEcũng không có tác dụng, truy vấn vẫn bị treo.
Kermit

2
Làm thế nào đến không có kế hoạch cho trường hợp thứ hai? Có phải chỉ vì truy vấn không bao giờ kết thúc? Nếu vậy bạn có thể có được một kế hoạch ước tính? Tự hỏi nếu các biểu thức khác nhau thay đổi ước tính cardinality và bạn kết thúc với một kế hoạch khác.
Martin Smith

3
Các ISNULLhình kế hoạch như nó đơn giản hoá tốt hơn. Nó có một vị từ bình đẳng đơn giản trên ColleagueList [Date]='2013-06-01'trong khi CASEcái đó có một vị từ [Date]>='2013-06-01' AND [Date]<=CASE WHEN (1) THEN '2013-06-01' ELSE NULL END AND PROBE([Bitmap1067],[Date]). Các hàng ước tính sắp ra khỏi liên kết đó là 28.393 cho ISNULLphiên bản nhưng thấp hơn nhiều 79.8đối với CASEphiên bản có ảnh hưởng đến lựa chọn tham gia sau này trong kế hoạch. Không chắc chắn tại sao sẽ có sự khác biệt như vậy.
Martin Smith
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.