我正在尝试完全解决以下问题完全在SQL (ANSI或TSQL,在Sybase ASE 12中),而不依赖于游标或基于循环的逐行处理。
注意:我已经创建了一个在应用程序层实现相同目标的解决方案(因此请不要“回答”“不要在SQL中执行此操作”),但这是一个原则问题(希望如此)改进的性能)我想知道是否有一个高效的(例如没有游标)纯SQL解决方案。
设置:
我有一个包含以下3列的表T(全部为NOT NULL):
---- Table T -----------------------------
| item | tag | value |
| [int] | [varchar(10)] | [varchar(255)] |
该表在item, tag
每个标签都有一个字符串形式“TAG ##”,其中“##”是数字1-99
不保证现有标签是连续的,例如第13项可能有标签“TAG1”,“TAG3”,“TAG10”。
任务:我需要从另一个表T_NEW中插入一堆新行,这些表只包含项目和值,并为它们分配新标记,因此它们不会违反item, tag
上的唯一索引。
值的唯一性无关紧要(假设项目+值始终是唯一的)。
---- Table T_NEW --------------------------
| item | tag | value |
| [int] | STARTS AS NULL | [varchar(255)] |
问题:如何为表T_NEW中的所有行分配新标记,例如:
T和T_NEW联合中的所有项目+标签组合都是唯一的
新分配的标签应全部采用“TAG ##”形式
新分配的标签理想情况下应该是给定项目的最小标签。
如果有帮助,您可以假设我已经有一个临时表#tags
,其中“tag”列包含99行,其中包含所有有效标记(TAG1..TAG99,每行一个) )
答案 0 :(得分:0)
我开始了一个fiddle,它会按项目为您提供可用的“打开”标签列表。它使用#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
或看到此updated fiddle在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
更新了fiddle,其中包含的mans row_number替换只能用于不同的T_New值
答案 1 :(得分: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
答案 2 :(得分:-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 ;
答案 3 :(得分:-1)
好的,这是一个正确的解决方案,经过测试可以在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