8

我对一个表有一个简单的查询,它返回如下结果:

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。

4

7 回答 7

6

由于在 Sybase 中使用 select 语句来完成这项工作相当困难,因此我建议使用while如下所示的循环。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
于 2010-06-04T07:15:56.620 回答
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
于 2010-06-03T12:16:36.390 回答
2

好的,如果我在这里遗漏了一些重要的东西,请原谅我,因为我不知道关于 Sybase 的第一件事。但是在 mysql 中,这非常简单,所以我认为它不会像迄今为止的答案那么糟糕。因此,从可能相关或不相关的文档中提取:

SELECT id, LIST(id_type) + ":" + LIST(id_ref) AS concatentation

如果我误读了某些内容,请通知我,我将删除它。

于 2010-06-05T03:10:36.997 回答
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
)
于 2011-10-12T06:24:47.347 回答
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
于 2010-05-31T11:47:39.000 回答
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

但结果与您键入的结果有点不同......!!!

于 2010-06-03T10:59:33.723 回答
0

我没有要测试的sybase 服务器,但是在线阅读文档,似乎支持公用表表达式。我不确定其他解决方案中使用的 ROW_NUMBER,所以这里有一个不使用它的解决方案。

我相信sybase使用|| 对于字符串连接,尽管我阅读的文档提到也可以使用“+”,所以我使用了它。请酌情更改。

我已经评论了查询以试图解释发生了什么。

该查询将所有具有相同 id 的 id_type 和 id_ref 值以“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 语句。

于 2010-06-03T21:34:41.680 回答