1

整数のリストを値のテーブルに分割するユーザー定義関数があります。これを使用して入力を解析し、特定のタイプまたはステータスのセットのレコードのセットを選択しています。

これは機能します:

select * from RequestStatus
where RequestStatusUID in (select [value] from dbo.SplitIDs('1,2,3', ','))

これはしません:

select * from Requests
where RequestStatusUID in (select [value] from dbo.SplitIDs('1,2,3', ','))

Requestsクエリは、「varchar値「1,2,3」をデータ型intに変換するときに変換に失敗しました。 」というエラーを返します。両方のテーブルのRequestStatusUIDはint列です。Explainの両方の計画は私には同じように見えます。この関数は、無関係のクエリでもまったく同じように機能します。私が知る限り、問題があるのはRequestsテーブルだけです。

CREATE TABLE [dbo].[Requests]  ( 
[RequestUID]        int IDENTITY(1,1) NOT NULL,
[UserUID]           int NOT NULL,
[LocationUID]       int NOT NULL,
[DateOpened]        date NULL,
[DateClosed]        date NULL,
[RequestStatusUID]  int NOT NULL,
[DiscussionUID]     int NULL,
[RequestTypeUID]    int NOT NULL,
[RequestNo]         varchar(16) NOT NULL,
[LastUpdateUID]     int NOT NULL,
[LastUpdated]       date NOT NULL,
CONSTRAINT [PK_Requests] PRIMARY KEY NONCLUSTERED([RequestUID])

varcharsを返す別の関数を使用し、RequestStatusUID列もvarcharに変換すると機能します。

select * from Requests
where cast(RequestStatusUID as varchar(4)) in (select [value] from dbo.Split('1,2,3', ','))

参考までに、私が使用しているSplitIDs関数(Arnold Fribbleのソリューションの修正バージョン)。Split関数は、キャストがなくても最後のintと同じです。

ALTER FUNCTION [dbo].[SplitIDs] ( @str VARCHAR(MAX), @delim char(1)=',' )
RETURNS TABLE
AS
RETURN
(
    with cte as (
        select 0 a, 1 b
        union all
        select b, cast(charindex(@delim, @str, b) + 1 as int)
        from cte
        where b > a
    )
    select cast(substring(@str,a,
    case when b > 1 then b-a-1 else len(@str) - a + 1 end) as int) [value]      
    from cte where a >0
)

文字列に変換するソリューションを使用できますが、そもそもなぜこれが失敗するのかを知りたいのです。

4

1 に答える 1

2

この構文の方がはるかに優れていることがわかると思います。

SELECT r.* FROM dbo.Requests AS r
INNER JOIN dbo.SplitIDs('1,2,3', ',') AS s
ON r.RequestStatusUID = s.value;

関数の選択により、述語にはまだ多数の暗黙的な変換がありますが、結合によって高価なテーブルスプールが排除されます。を使用する代わりに、必要な実際の列に限定された適切な列リストを使用すると、パフォーマンスがわずかに向上する場合がありますSELECT *すべての列が必要な場合でも、これを変更する必要があります

高価IN ()なテーブルスプールを使用したクエリ(クリックして拡大):

ここに画像の説明を入力してください

私のJOINバージョンでは、コストはとにかく実行しているスキャンに転送されます(クリックして拡大):

ここに画像の説明を入力してください

そして、ここに実行時のメトリックがあります(もちろん少数の行に基づいています)-(クリックして拡大):

ここに画像の説明を入力してください

変換エラーは関数に起因しているようです。だから私は自分のものに置き換えました(以下)。最初は知らなかった外部キーを追加しても、エラーを再現できませんでした。元の関数の問題が正確に何であるかはわかりませんが、それが作成するすべての暗黙の変換は、ある時点でオプティマイザーに問題を引き起こすようです。だから私は代わりにこれを提案します:

CREATE FUNCTION dbo.SplitInts
(
   @List       VARCHAR(MAX),
   @Delimiter  VARCHAR(255) = ','
)
RETURNS TABLE
WITH SCHEMABINDING
AS
   RETURN 
   (  
      SELECT [value] = y.i.value('(./text())[1]', 'int')
      FROM 
      ( 
        SELECT x = CONVERT(XML, '<i>' 
          + REPLACE(@List, @Delimiter, '</i><i>') 
          + '</i>').query('.')
      ) AS a CROSS APPLY x.nodes('i') AS y(i)
   );
GO

だから、あなたが関数を取り除きたいように私には思えます。

また、結合を使用し、パラメータをオプションにする1つの方法は次のとおりです。

DECLARE @param VARCHAR(MAX) = NULL;-- also try = '1,2,3';

SELECT r.*
FROM dbo.Requests AS r
LEFT OUTER JOIN dbo.SplitInts(@param, default) AS s
ON r.RequestStatusUID = s.value
WHERE (r.RequestStatusUID = s.value OR @param IS NULL);
于 2013-03-26T20:15:04.200 に答える