Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/78.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在SQL中有没有一种很好的方法可以做到这一点?_Sql_Tsql_Unique Constraint_Sap Ase - Fatal编程技术网

在SQL中有没有一种很好的方法可以做到这一点?

在SQL中有没有一种很好的方法可以做到这一点?,sql,tsql,unique-constraint,sap-ase,Sql,Tsql,Unique Constraint,Sap Ase,我试图完全用SQL(Sybase ASE 12中的ANSI或TSQL)解决以下问题,而不依赖于游标或基于循环的逐行处理 注意:我已经创建了一个在应用层实现相同目标的解决方案(因此请不要用“请勿在SQL中执行此操作”来回答),但作为原则问题(并希望提高性能),我想知道是否有一个高效的(例如,没有游标)纯SQL解决方案 设置: 我有一个包含以下3列的表T(所有列都不为空): 该表在项、标记上有唯一索引 每个标记都有一个字符串“tag###”的形式,其中“###”是一个数字1-99 现有标签不保证

我试图完全用SQL(Sybase ASE 12中的ANSI或TSQL)解决以下问题,而不依赖于游标或基于循环的逐行处理

注意:我已经创建了一个在应用层实现相同目标的解决方案(因此请不要用“请勿在SQL中执行此操作”来回答),但作为原则问题(并希望提高性能),我想知道是否有一个高效的(例如,没有游标)纯SQL解决方案

设置

  • 我有一个包含以下3列的表T(所有列都不为空):

  • 该表在
    项、标记上有唯一索引

  • 每个标记都有一个字符串“tag###”的形式,其中“###”是一个数字1-99

  • 现有标签不保证连续,例如项目13可能有标签“TAG1”、“TAG3”、“TAG10”

  • 任务:我需要从另一个只包含项和值的表T\u new向表中插入一组新行,并为它们分配新标记,这样它们就不会违反
    项、标记
    上的唯一索引

    值的唯一性是不相关的(假定项+值始终是唯一的)

  • 问题:如何将新标记分配给表T_new中的所有行,以便:

    • T和T_NEW并集中的所有项目+标记组合都是唯一的

    • 新分配的标签应全部采用“标签”的形式

    • 理想情况下,新分配的标记应该是给定项目可用的最小标记

  • 如果有帮助,您可以假设我已经有一个临时表
    #tags
    ,其中的“tag”列包含99行,其中包含所有有效的标记(TAG1..TAG99,每行一个)

直接插入到
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\u 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 ;
我启动了一个程序,它将按项目为您提供可用的“打开”标记列表。它使用#标记(AllTags)并在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_新表。本质上是添加一个行号,这样我们就可以在单个update语句中选择正确的open标记。我用前导零填充标记名以简化排序

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

更新了poor mans row_编号替换,该替换仅适用于不同的T_新值

好的,这里有一个正确的解决方案,经过测试可在Sybase上使用(H/T:非常感谢@ypercube为其提供了坚实的基础)

试试这个:

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

所以你想从最低的可用标签号开始,并填补空白?@dotjoe-差不多。不一定要100%完美,但如果TAG1-96是免费的,我不想以“TAG97”开始,并在该项的4行之后用完标记。对不起,我没有要测试的Sybase安装。它可能不喜欢它们作为内联子查询。我们可以在
FROM
子句中移动它们,可能是通过
groupby
来移动。是的,我认为在Sybase中可以使用
TOP
。我之所以这么说,是因为它比
MAX()
更灵活(排名靠前+靠后)。我很抱歉,我无法接受并投了反对票。我刚刚意识到,当t_new table有>1行具有相同的项时,这不起作用(它应该为这些行中的每一行分配不同的标记,但事实并非如此)。正如Q所说:“T和T_NEW联合中的所有物品+标签组合都是唯一的”@DVK:啊,是的,你是对的。我将用新版本更新。我不确定…我以前从未使用过sybase。我的问题对sybase非常具体。Oraclish/MSSQL解决方案并没有真正帮助我:(很抱歉,我看到了tsql,不再查看标记。从我搜索的内容来看,Sybase支持CTE和窗口函数。Sybase 12似乎不支持交叉连接-例如,我甚至看到有人提到Sybase 15不支持它,但不支持certain@dotjoe-如果您仔细阅读问题,则要求的不是避免循环,而是避免基于循环的循环逐行处理(例如,无光标或其模拟)。此循环远未接近逐行-其迭代次数最大(计数(*)当按item.IOW分组时,如果每个项最多有5个重复项,那么即使有100000个项,循环也将执行5次。即使最坏的情况是100次迭代,因为我们最多只能使用100个标记。
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
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
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