4

次の問題を完全に SQL (Sybase ASE 12 では ANSI または TSQL) で解決しようとしていますが、カーソルやループベースの行ごとの処理に頼ることはありません。

注:アプリケーション層で同じ目標を達成するソリューションをすでに作成しました(したがって、「SQLでこれをしないでください」と「答える」ことは控えてください)が、原則として(そしてうまくいけばパフォーマンスが向上します)効率的な (カーソルがないなどの) 純粋な SQL ソリューションがあるかどうかを知る。

セットアップ:

  • 次の3つの列を持つテーブルTがあります(すべてNULLではありません):

    ---- Table T -----------------------------
    | item  | tag           | value          | 
    | [int] | [varchar(10)] | [varchar(255)] | 
    
  • テーブルには一意のインデックスがありますitem, tag

  • すべてのタグは文字列「TAG##」の形式を持ち、「##」は 1 ~ 99 の数字です

  • 既存のタグは連続しているとは限りません。たとえば、アイテム 13 には「TAG1」、「TAG3」、「TAG10」というタグが含まれている場合があります。

  • TASK :項目と値のみを持つ別のテーブル T_NEW から一連の新しい行をテーブルに挿入し、それらに新しいタグを割り当てて、一意のインデックスに違反しないようにする必要がありますitem, tag

    値の一意性は関係ありません (アイテム + 値は常に一意であると仮定します)。

    ---- Table T_NEW --------------------------
    | item  | tag            | value          | 
    | [int] | STARTS AS NULL | [varchar(255)] | 
    
  • 質問: 次のように、テーブル T_NEW のすべての行に新しいタグを割り当てるにはどうすればよいですか?

    • T と T_NEW の和集合内のすべての項目とタグの組み合わせは一意です

    • 新しく割り当てられたタグはすべて「TAG##」の形式である必要があります

    • 新しく割り当てられたタグは、理想的には、特定のアイテムで利用可能な最小のものであるべきです。

  • それが役立つ場合は、#tagsすべての有効なタグ (TAG1..TAG99、行ごとに 1 つ) を含む 99 行を含む「タグ」列を持つ一時テーブルが既にあると想定できます。

4

4 に答える 4

0

これを試して:

DECLARE @T TABLE (ITEM  INT, TAG VARCHAR(10), VALUE VARCHAR(255))
INSERT INTO @T VALUES 
(1,'TAG1', '100'),
(2,'TAG2', '200')

DECLARE @T_NEW TABLE (ITEM  INT, TAG VARCHAR(10), VALUE VARCHAR(255))
INSERT INTO @T_NEW VALUES 
(3,NULL, '500'),
(4,NULL, '600')

INSERT INTO @T
SELECT
    ITEM,
    ('TAG' + CONVERT(VARCHAR(20),ITEM)) AS TAG,
    VALUE
FROM 
   @T_NEW

SELECT * FROM @T
于 2013-10-17T09:31:53.833 に答える
0

アイテムごとに利用可能な「オープン」タグのリストを取得するフィドルを開始しました。#tags (AllTags) を使用してこれを行い、outer-join-where-null. これを使用して、T_New から新しいタグを挿入できます...

with T_openTags as (
  select 
    items.item,
    openTagName = a.tag
  from
    (select distinct item from T) items
    cross join AllTags a
    left outer join T on 
      items.item = T.item
      and T.tag = a.tag
  where
    T.item is null
 )

select * from T_openTags

または、この更新されたフィドルを参照して、T_New テーブルを更新してください。基本的に、単一の更新ステートメントで使用する正しい開始タグを選択できるように、row_number を追加します。並べ替えを簡単にするために、タグ名の先頭にゼロを追加しました。

with T_openTags as (
  select 
    items.item,
    openTagName = a.tag,
    rn = row_number() over(partition by items.item order by a.tag)
  from
    (select distinct item from T) items
    cross join AllTags a
    left outer join T on 
      items.item = T.item
      and T.tag = a.tag
  where
    T.item is null

), T_New_numbered as (

  select *, 
     rn = row_number() over(partition by item order by value) 
  from T_New
)

update tnn set tag = openTagName
from T_New_numbered tnn
inner join T_openTags tot on 
  tot.item = tnn.item
  and tot.rn = tnn.rn


select * from T_New

個別の T_New 値でのみ機能する貧弱な人の row_number 置換で更新されたフィドル

于 2013-02-13T19:43:19.417 に答える
-1

に直接挿入t

INSERT INTO t
    (item, tag, value) 
SELECT 
    item, 
    ( SELECT MIN(tags.tag)
      FROM #tags AS tags
        LEFT JOIN t AS o
          ON  tags.tag = o.tag
          AND o.item_id = n.item_id 
      WHERE o.tag IS NULL
    ) AS tag,
    value  
FROM
    t_new AS n ;

更新t_new

UPDATE
    t_new AS n
SET
    tag =  
    ( SELECT MIN(tags.tag)
      FROM #tags AS tags
        LEFT JOIN t AS o
          ON  tags.tag = o.tag
          AND o.item_id = n.item_id 
      WHERE o.tag IS NULL
    ) ;

修正

UPDATE
    n
SET
    n.tag = w.tag
FROM
    ( SELECT item_id,
             tag,
             ROW_NUMBER() OVER (PARTITION BY item_id ORDER BY value) AS rn
      FROM t_new
    ) AS n
  JOIN
    ( SELECT di.item_id,
             tags.tag,
             ROW_NUMBER() OVER (PARTITION BY di.item_id ORDER BY tags.tag) AS rn
      FROM 
          ( SELECT DISTINCT item_id
            FROM t_new
          ) AS di
        CROSS JOIN 
          #tags AS tags
        LEFT JOIN
          t AS o
            ON  tags.tag = o.tag
            AND o.item_id = di.item_id 
      WHERE o.tag IS NULL
    ) AS w
    ON  w.item_id = n.item_id
    AND w.rn = n.rn ;
于 2013-02-13T17:36:51.093 に答える
-1

OK、これが正しいソリューションであり、Sybaseで動作するようにテストされています(H / T:@ypercubeにしっかりとした基盤を提供してくれてありがとう)

declare @c int
select @c = 1
WHILE (@c > 0)
BEGIN

    UPDATE
        t_new
    SET
        tag =  
        ( SELECT min(tags.tag)
          FROM #tags tags
            LEFT JOIN t o
              ON  tags.tag = o.tag
              AND o.item = t_new.item
            LEFT JOIN t_new n3
              ON  tags.tag = n3.tag
              AND n3.item = t_new.item
          WHERE o.tag IS NULL
          AND n3.tag IS NULL
        )
        WHERE tag IS NULL
        -- and here's the main magic for only updating one item at a time
        AND NOT EXISTS (SELECT 1 FROM t_new n2 WHERE t_new.value > n2.value 
                        and n2.tag IS NULL and n2.item=t_new.item)
        SELECT @c = @@rowcount
END
于 2013-02-13T21:19:42.990 に答える