1

I have a table EmployeeMoves:

| EmployeeID         | CityIDs    
+------------------------------
| 24                 | 23,21,22 
| 25                 | 25,12,14 
| 29                 | 1,2,5 
| 31                 | 7 
| 55                 | 11,34 
| 60                 | 7,9,21,23,30 

I'm trying to figure out how to expand the comma-delimited values from the EmployeeMoves.CityIDs column to populate an EmployeeCities table, which should look like this:

| EmployeeID         | CityID    
+------------------------------
| 24                 | 23 
| 24                 | 21 
| 24                 | 22 
| 25                 | 25 
| 25                 | 12 
| 25                 | 14 
| ... and so on

I already have a function called SplitADelimitedList that splits a comma-delimited list of integers into a rowset. It takes the delimited list as a parameter. The SQL below will give me a table with split values under the column Value:

select value from dbo.SplitADelimitedList ('23,21,1,4');

| Value   
+----------- 
| 23          
| 21        
| 1       
| 4  

The question is: How do I populate EmployeeCities from EmployeeMoves with a single (even if complex) SQL statement using the comma-delimited list of CityIDs from each row in the EmployeeMoves table, but without any cursors or looping in T-SQL? I could have 100 records in the EmployeeMoves table for 100 different employees.

4

2 に答える 2

3

これが私がこの問題を解決しようとした方法です。動作しているようで、パフォーマンスが非常に高速です。

INSERT INTO EmployeeCities
SELECT
    em.EmployeeID,
    c.Value
FROM EmployeeMoves em
CROSS APPLY dbo.SplitADelimitedList(em.CityIDs) c;

更新 1:

この更新では、ユーザー定義関数 dbo.SplitADelimitedList の定義が提供されます。この関数は、上記のクエリで使用され、カンマ区切りのリストを整数値のテーブルに分割します。

  CREATE FUNCTION dbo.fn_SplitADelimitedList1
   (
      @String NVARCHAR(MAX)
   )
  RETURNS @SplittedValues TABLE(
   Value INT
  )
 AS
 BEGIN
  DECLARE @SplitLength INT
  DECLARE @Delimiter VARCHAR(10) 
  SET @Delimiter = ',' --set this to the delimiter you are using

  WHILE len(@String) > 0
   BEGIN
    SELECT @SplitLength = (CASE charindex(@Delimiter, @String)
         WHEN 0 THEN
           datalength(@String) / 2
         ELSE
           charindex(@Delimiter, @String) - 1
       END)

   INSERT INTO @SplittedValues
   SELECT cast(substring(@String, 1, @SplitLength) AS INTEGER)
   WHERE
   ltrim(rtrim(isnull(substring(@String, 1, @SplitLength), ''))) <> '';

   SELECT @String = (CASE ((datalength(@String) / 2) - @SplitLength)
         WHEN 0 THEN
           ''
         ELSE
           right(@String, (datalength(@String) / 2) - @SplitLength - 1)
       END)

  END

 RETURN

END
于 2013-06-11T23:38:49.897 に答える
-3

序文

これは正しい方法ではありません。SQL Server でコンマ区切りのリストを作成しないでください。これは、信じられないほど卑劣な罵倒のように聞こえるはずの第 1 正規形に違反しています。

クライアント側のアプリケーションで、従業員と関連する都市の行を選択し、これをコンマ区切りのリストとして表示するのは簡単です。データベースでは実行しないでください。今後このような工事を行わないよう、万全を尽くしてください。可能であれば、データベースをリファクタリングする必要があります。

正しい答え

都市のリストを含むテーブルから適切に展開された都市のリストを取得するには、次のようにします。

INSERT dbo.EmployeeCities
SELECT
    M.EmployeeID,
    C.CityID
FROM
   EmployeeMoves M
   CROSS APPLY dbo.SplitADelimitedList(M.CityIDs) C
;

間違った答え

あなたが望んでいたことを誤解したため、この回答を書きました。適切に保存されたデータに対してクエリを実行して、カンマ区切りの CityID のリストを作成しようとしていると思いました。しかし、今では逆のことを望んでいたことに気付きました。列に既に格納されている既存のコンマ区切りの値を使用して、都市のリストをクエリすることです。

WITH EmployeeData AS (
   SELECT
      M.EmployeeID,
      M.CityID
   FROM
      dbo.SplitADelimitedList ('23,21,1,4') C
      INNER JOIN dbo.EmployeeMoves M
         ON Convert(int, C.Value) = M.CityID
)
SELECT
   E.EmployeeID,
   CityIDs = Substring((
      SELECT ',' + Convert(varchar(max), CityID)
      FROM EmployeeData C
      WHERE E.EmployeeID = C.EmployeeID
      FOR XML PATH (''), TYPE
   ).value('.[1]', 'varchar(max)'), 2, 2147483647)
FROM
   (SELECT DISTINCT EmployeeID FROM EmployeeData) E
;

私の理解の難しさの一部は、あなたの質問が少しまとまりがないことです. 次回は、サンプル データに明確なラベルを付けて、何を持っているか、何を目指しているかを示してくださいEmployeeCities のデータを最後に配置したため、達成しようとしているように見えました。質問がうまく配置されていない場合、人々の時間を有効に使うことはできません。

于 2013-06-11T21:58:42.293 に答える