我对表进行简单查询,返回如下结果:
id id_type id_ref
2702 5 31
2702 16 14
2702 17 3
2702 40 1
2703 23 4
2703 23 5
2703 34 6
2704 1 14
我想将结果合并为一行,例如:
id concatenation
2702 5,16,17,40:31,14,3,1
2703 23,23,34:4,5,6
2704 1:14
有没有办法在触发器中执行此操作?
注意:我知道我可以使用光标,但我真的不愿意,除非没有更好的方法。
数据库是Sybase 12.5.4版。
答案 0 :(得分:6)
由于使用select语句在Sybase中完成此操作相当困难,我建议使用while
循环,如下所示。虽然循环优于游标,但速度要快得多。假设表名是MYTABLE:
CREATE TABLE #temp
(
aa numeric(5,0) identity,
id int not null,
id_type int not null,
id_ref int not null
)
CREATE TABLE #results
(
id int not null,
concatenation varchar(1000) not null,
)
insert into #temp
select id, id_type, id_ref from MYTABLE order by id
declare @aa int, @maxaa int, @idOld int, @idNew int
declare @str1 varchar(1000), @str2 varchar(1000)
set @aa = 1
set @maxaa = (select max(aa) from #temp)
set @idNew = (select id from #temp where aa = 1)
, @idOld = @idNew
while @aa <= @maxaa
begin
set @idNew = (select id from #temp where aa = @aa)
IF @idNew = @idOld
BEGIN
set @str1 = @str1 + convert(varchar,(select id_type from #temp where aa = @aa)) + ','
, @str2 = @str2 + convert(varchar,(select id_ref from #temp where aa = @aa)) + ','
IF @aa = @maxaa
insert into #results (id, concatenation)
VALUES (@idOld, left(@str1,len(@str1) - 1) + ':' + left(@str2,len(@str2) - 1) )
END
ELSE
BEGIN
insert into #results (id, concatenation)
VALUES (@idOld, left(@str1,len(@str1) - 1) + ':' + left(@str2,len(@str2) - 1) )
set @str1 = NULL, @str2 = NULL
set @str1 = @str1 + convert(varchar,(select id_type from #temp where aa = @aa)) + ','
, @str2 = @str2 + convert(varchar,(select id_ref from #temp where aa = @aa)) + ','
IF @aa = @maxaa
insert into #results (id, concatenation)
VALUES (@idNew, left(@str1,len(@str1) - 1) + ':' + left(@str2,len(@str2) - 1) )
END
set @idOld = @idNew
set @aa = @aa+1
end
select * from #results
修改强> 以下版本的速度提高了约45%
CREATE TABLE #temp
(
aa numeric(5,0) identity,
id int not null,
id_type int not null,
id_ref int not null
)
CREATE TABLE #results
(
id int not null,
concatenation varchar(1000) not null,
)
insert into #temp
select id, id_type, id_ref from MYTABLE order by id
declare @aa int, @maxaa int, @idOld int, @idNew int
declare @str1 varchar(1000), @str2 varchar(1000), @j int
set @aa = 1
set @maxaa = (select max(aa) from #temp)
set @idNew = (select id from #temp where aa = 1)
, @idOld = @idNew
set @str1 = ':'
while @aa <= @maxaa
begin
set @idNew = (select id from #temp where aa = @aa)
IF @idNew = @idOld
BEGIN
set @str2 = (select convert(varchar,id_type) + ':' + convert(varchar,id_ref) from #temp where aa = @aa)
set @j = (select charindex(':',@str2))
set @str1 = str_replace(@str1, ':', substring(@str2,1,@j - 1) + ',:') + right(@str2,len(@str2) - @j) + ','
IF @aa = @maxaa
insert into #results (id, concatenation)
VALUES (@idOld, left(str_replace(@str1, ',:', ':'),len(@str1) - 2) )
END
ELSE
BEGIN
insert into #results (id, concatenation)
VALUES (@idOld, left(str_replace(@str1, ',:', ':'),len(@str1) - 2) )
set @str1 = ':'
set @str2 = (select convert(varchar,id_type) + ':' + convert(varchar,id_ref) from #temp where aa = @aa)
set @j = (select charindex(':',@str2))
set @str1 = str_replace(@str1, ':', substring(@str2,1,@j - 1) + ',:') + right(@str2,len(@str2) - @j) + ','
IF @aa = @maxaa
insert into #results (id, concatenation)
VALUES (@idNew, left(str_replace(@str1, ',:', ':'),len(@str1) - 2) )
END
set @idOld = @idNew
set @aa = @aa+1
end
select * from #results
答案 1 :(得分:2)
另一种适用于Sybase ASE 12.5.4的方法。该表必须在id上具有聚簇索引,以使其起作用。假设表名是MYTABLE:
declare @strNew varchar(10), @strOld varchar(10), @str1 varchar(1000), @str2 varchar(1000)
set @str1 = NULL, @str2 = NULL, @strNew = NULL, @strOld = NULL
UPDATE MYTABLE
SET @strNew = convert(varchar,id)
, @str1 = case when @strNew = @strOld then @str1 + convert(varchar,id_type) + "," else @str1 + '$' + @strNew + '$' + convert(varchar,id_type) + "," end
, @str2 = case when @strNew = @strOld then @str2 + convert(varchar,id_ref) + "," else @str2 + '$' + @strNew + '$' + convert(varchar,id_ref) + "," end
, @strOld = convert(varchar,id)
select id, substring(@str1,charindex("$" + convert(varchar,id) + "$",@str1) + len("$" + convert(varchar,id) + "$"),
case when
charindex(",$",substring(@str1,charindex("$" + convert(varchar,id) + "$",@str1) + len("$" + convert(varchar,id) + "$") + 1,len(@str1)))
= 0 then len(@str1) - (charindex("$" + convert(varchar,id) + "$",@str1) + len("$" + convert(varchar,id) + "$"))
else
charindex(",$",substring(@str1,charindex("$" + convert(varchar,id) + "$",@str1) + len("$" + convert(varchar,id) + "$") + 1,len(@str1)))
end
)
+ ':' +
substring(@str2,charindex("$" + convert(varchar,id) + "$",@str2) + len("$" + convert(varchar,id) + "$"),
case when
charindex(",$",substring(@str2,charindex("$" + convert(varchar,id) + "$",@str2) + len("$" + convert(varchar,id) + "$") + 1,len(@str2)))
= 0 then len(@str2) - (charindex("$" + convert(varchar,id) + "$",@str2) + len("$" + convert(varchar,id) + "$"))
else
charindex(",$",substring(@str2,charindex("$" + convert(varchar,id) + "$",@str2) + len("$" + convert(varchar,id) + "$") + 1,len(@str2)))
end
) as concatenation
from MYTABLE
group by id
答案 2 :(得分:2)
使用行级功能。
您的查询:
select distinct id ,fn(id) from table1
功能:
fn(@id int)
(
declare @res varchar
select @res = @res+id_ref+"," from table1 where id=@id
return @res
)
答案 3 :(得分:1)
这是一个解决方案:
SELECT DISTINCT
id,
concatenation = LEFT(id_types, LEN(id_types) - 1) + ':' + LEFT(id_refs, LEN(id_refs) - 1)
FROM (
SELECT id,
id_types = (SELECT CAST(b.id_type AS nvarchar) + ',' FROM Table1 b WHERE b.id = a.id FOR XML PATH('')),
id_refs = (SELECT CAST(c.id_ref AS nvarchar) + ',' FROM Table1 c WHERE c.id = a.id FOR XML PATH(''))
FROM Table1 a
) t
更新:另一种方法
;WITH r(id, rnk, id_type, id_ref) AS
(
SELECT id,
rnk = ROW_NUMBER() OVER(ORDER BY id),
id_type = CAST(id_type AS nvarchar(MAX)),
id_ref = CAST(id_ref AS nvarchar(MAX))
FROM Table1
), anchor(id, rnk, id_type, id_ref) AS
(
SELECT id,
rnk,
id_type,
id_ref
FROM r
WHERE rnk = 1
), result(id, rnk, id_type, id_ref) AS
(
SELECT id,
rnk,
id_type,
id_ref
FROM anchor
UNION ALL
SELECT r.id,
r.rnk,
result.id_type + ',' + r.id_type,
result.id_ref + ',' + r.id_ref
FROM r
INNER JOIN result ON r.id = result.id AND r.rnk = result.rnk + 1
)
SELECT id, concatenation = MAX(id_type) + ':' + MAX(id_ref)
FROM result
GROUP BY id
答案 4 :(得分:1)
我现在能想到的最好的是下一个:
select a.id id,
str (a.id_type,4,0)||
','||str (b.id_type,4,0)||
','||str (c.id_type,4,0)||
','||str (d.id_type,4,0)||
','||str (e.id_type,4,0)||':'||
str (a.id_ref,4,0)||
','||str (b.id_ref,4,0)||
','||str (c.id_ref,4,0)||
','||str (d.id_ref,4,0)||
','||str (e.id_ref,4,0) concatenation
from dbo.merge_test a,
dbo.merge_test b,
dbo.merge_test c,
dbo.merge_test d,
dbo.merge_test e
where a.id = b.id
and a.id = b.id
and a.id = c.id
and a.id = d.id
and a.id = e.id
and a.id_type < b.id_type
and b.id_type <c.id_type
and c.id_type < d.id_type
and d.id_type < e.id_type
但结果与你输入的结果有点不同...... !!!
答案 5 :(得分:1)
好的,请原谅我,如果我错过了一些关键的东西,因为我不知道关于Sybase的第一件事。但是在mysql中,这非常简单,所以我认为它到目前为止还不如答案那么糟糕。因此,从可能相关或不相关的文档中提取:
SELECT id, LIST(id_type) + ":" + LIST(id_ref) AS concatentation
如果我误读了某些内容,请通知我,我会将其删除。
答案 6 :(得分:0)
我没有要测试的sybase服务器,但是在线阅读文档,似乎支持公共表表达式。我不确定ROW_NUMBER,正如其他解决方案中所使用的那样,所以这里有一个不使用它的解决方案。
我相信sybase使用||对于字符串连接,虽然我读过的文档提到“+”也可以使用,所以我用过它。请根据需要进行更改。
我评论了这个问题,试图解释发生了什么。
查询将所有id_type和id_ref值连接到相同的id,并以'id_type'顺序递增。
/* a common table expression is used to concatenate the values, one by one */
WITH ConcatYourTable([id], /* the id of rows being concatenated */
concat_id_type, /* concatenated id_type so far */
concat_id_ref, /* concatenated id_ref so far */
last_id_type, /* the last id_type added */
remain) /* how many more values are there to concatenate? */
AS
(
/* start with the lowest id_type value for some id */
SELECT id, id_type, id_ref,
id_type, /* id_type was concatentated (it's presently the only value) */
(SELECT COUNT(*) FROM YourTable f2 WHERE f2.id=f.id)-1
/* how many more values to concatenate -1 because we've added one already */
FROM YourTable f
WHERE NOT EXISTS
/* start with the lowest value - ensure there are no other values lower. */
(SELECT 1 FROM YourTable f2 WHERE f2.id=f.id AND f2.id_type<f.id_type)
UNION ALL
/* concatenate higher values of id_type for the same id */
SELECT f.id,
c.id_type + ',' + f.id_type, /* add the new id_type value to the current list */
c.id_ref + ',' + f.id_ref, /* add the new id_ref value to the current list */
f.id_type, /* the last value added - ensured subsequent added values are greater */
c.remain-1 /* one less value to add */
FROM ConcatYourTable c /* take what we have concatenated so far */
INNER JOIN YourTable f /* add another row with the same id, and > id_type */
ON f.id = c.id AND f.id_type > c.last_id_type
/* we really want the next highest id_type, not just one that is greater */
WHERE NOT EXISTS (SELECT 1 FROM YourTable f2
WHERE f2.id=f.id AND f2.id_type<f.id_type AND
f2.id_type>c.last_id_type)
)
/* Select the rows where all values for and id were concatenated (remain=0) */
/* Concatenate the cumulated id_type and id_ref fields to format id_type values:id_ref values*/
SELECT id, id_type+':'+id_ref FROM ConcatYourTable
WHERE remain=0
该查询非常“野蛮”,因为它不使用可能提高可读性或可能性能的更复杂功能。我已经这样做了,因为我不熟悉sybase,并且使用了那些我有理由相信的功能。为了获得最佳性能,请确保将id和(id,id_type)编入索引。
要在触发器中使用它,例如INSERT或UPDATE触发器,以根据此并置查询维护表,请扩展基本案例的WHERE子句(在UNION ALL之前)以包括id = @ changed_id。这将确保仅计算更改的id的连接行。然后,您可以使用计算的连接行执行所需操作。如果要将连接查询具体化为表,则在表中删除@changed_id的当前连接行,并从上面的连接查询的结果中插入新行。您还可以检查您的连接表是否已包含changed_id的值,并使用UPDATE语句。