我们正在尝试在Sybase中创建一个SQL查询,我们可以将多个行连接到一个选定的行中。
布局:
| Type | Skill |
----------------
| A | 1
| A | 2
| B | 1
ETC
我希望输出如下:A(1,2)
答案 0 :(得分:0)
这是哪种特定类型的Sybase数据库?如果是ASE,那么你要么必须使用循环方法(例如Cursor),要么使用局部变量在UPDATE语句中使用一些有点奇特的技巧(这里描述的内容太多了,但是详细信息在我的书中提示, sybase ase'
的技巧和方法答案 1 :(得分:-1)
如果使用ASE 16,您可以滚动自己的函数来模拟list()函数;请参阅此主题中的回复:Implementing group_concat() in ASE
请注意Edison对KBA 2260479的回应...在交易中使用表变量时的错误。
-----------根据建议从sap.com链接复制相关信息......
参考示例@ Percona example(对不起,也不会复制该链接;此帖子附带的示例显示了匹配percona示例的结果)
要在ASE中模仿MySQL的GROUP_CONCAT()函数,我们需要了解一些问题/限制/观察:
ASE不允许聚合/ UDF(例如,将结果集作为参数传递给函数)
ASE不允许使用表变量定义UDF作为输入参数
ASE不允许在UDF中创建#temp表
ASE 16 SP02 + DOES 允许在UDF中创建/使用表变量
ASE DOES 允许在UDF中使用exec()构造
GROUP_CONCAT()参数由要追加的列/字符串组成,'顺序由'组成。子句和可选的SEPARATOR(见上文percona.com链接中的示例);不难看出列/字符串+'顺序排列'子句是SELECT查询的组件
我们可以通过传递表示所需数据集的SQL / SELECT查询来模拟将数据集传递给UDF;然后可以通过exec()构造执行此SQL / SELECT查询,为UDF提供所需的数据集
我们的UDF设计的要点:
注意:由于我们使用表变量,因此需要ASE 16.0 SP02 +
1 - UDF的输入参数 - @sql varchar(XXX) - 表示调用进程提供的SQL / SELECT语句
1a- @sql必须是一个完整的独立查询(即,您应该能够在单独的ASE会话中自行运行查询)
1b - @sql的选择/投影列表与表变量的列匹配(参见下一个项目符号)
1c - @sql包括任何必要的'组/顺序'子句(即UDF不会执行任何排序操作)
2 - UDF创建一个表变量,其列定义为保存@sql查询的结果
2a - 表变量的列必须与数据类型明智地匹配@sql查询的选择/投影列表
3 - UDF通过exec()结构填充表变量:
exec("insert @table_var " + @sql)
4 - UDF使用游标循环遍历表变量
中的记录4a - 光标没有'顺序' clause =>假设在插入表变量时,基于@sql
对行进行排序有关此特定UDF的一些细节:
1 - 我已经在sybsystemprocs数据库中创建了UDF并将其命名为:
sp_f_group_concat
1a - ' sp _'前缀意味着UDF可以在任何数据库中执行
1b - ' f _' string允许我快速/直观地看到这是一个函数而不是系统存储过程
2 - 创建UDF时假设传入的SQL / SELECT查询将具有由单个varchar(100)列组成的选择/投影列表
2a - 调用进程需要执行任何必要的数据类型转换(到char)以及列/字符串的任何连接
2b - @sql输入参数已定义为varchar(1000),@ separator已定义为varchar(10),默认值为单个逗号(&#39;,&#39;)< / p>
2c - UDF的所有者需要根据他们期望在其环境中处理的内容来修改varchar()长度
3 - 由于UDF无法确定TF:7703(允许按行累积数据到单个@变量),并且UDF不执行任何排序,我们将使用游标单步执行表变量
中的记录4 - 从percona.com示例中可以看出,MySQL的GROUP_CONCAT()函数如何处理附加非NULL值的NULL(例如,忽略NULL?是NULL转换为空字符串&#39;?是将NULL转换为字符串&#39; NULL&#39;?);最终结果是UDF所有者/用户可能需要重新访问UDF和/或@sql设计,如果他们发现没有按需处理NULL
嗯,无法将文件附加到stackoverflow帖子?好的,所以切割粘贴它是... yuck,不完全是源的样子......
+++++++++++++++++++++++++++++++++++++++++++++++++
use sybsystemprocs
go
if object_id('sp_f_group_concat') is not null
drop function sp_f_group_concat
go
create function sp_f_group_concat
(@sql varchar(1000)
,@separator varchar(10) = NULL
)
returns varchar(1000)
as
/*
sp_f_group_concat
ASE implementation of MySQL's GROUP_CONCAT() function.
See https://ideas.sap.com/D36082 for a discussion of this topic, along
with some examples (as attachments to one of Mark's comments)
Requirements/Assumptions
========================
- ASE version must support a) user defined functions and b) table variables
- @sql is a standalone query that generates a result set consisting of a single varchar column
- @sql includes an 'order by' clause if needed (ie, this function does not attempt to order the results generated by @sql)
History
=======
10/10/2016 Mark A. Parsons Initial coding
*/
set nocount on
declare @string varchar(100),
@string_list varchar(1000)
-- default delimiter to ',' if not supplied
select @separator = isnull(@separator,',')
-- create/populate @strings table
declare @strings table (string varchar(100))
exec("insert @strings " + @sql)
-- assume TF:7703 is not enabled, so can't use a single SELECT to append to a @variable
-- assume @sql has a 'order by' clause and that our cursor will pull from @strings in the same order
declare string_cur cursor
for
select string
from @strings
for read only
-- loop through @strings rows, appending individual strings to @string_list
open string_cur
fetch string_cur into @string
while @@sqlstatus = 0
begin
select @string_list = @string_list + case when @string_list is not NULL then @separator end + @string
fetch string_cur into @string
end
close string_cur
deallocate cursor string_cur
-- send concatenated list of strings back to calling process
return @string_list
go
grant execute on sp_f_group_concat to public
go
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
use tempdb
go
set nocount on
go
/*
reproduction of the MySQL/GROUP_CONCAT() examples from:
https://www.percona.com/blog/2013/10/22/the-power-of-mysql-group_concat/
*/
if object_id('group_c') is not NULL
drop table group_c
go
create table group_c
(parent_id int NULL
,child_id int NULL
)
go
insert group_c values (1,1)
insert group_c values (1,1)
insert group_c values (1,2)
insert group_c values (1,3)
insert group_c values (1,4)
insert group_c values (2,1)
insert group_c values (2,4)
insert group_c values (2,6)
insert group_c values (3,1)
insert group_c values (3,2)
insert group_c values (4,1)
insert group_c values (4,1)
insert group_c values (5,0)
go
-----
print "
******************************
QUERY # 1 : List of parents and associated children (default separator = ',')
******************************
"
select parent_id,
-- we know child_id_list is relatively narrow in this case
-- so reduce the width of the output via left(--,20)
left( dbo.sp_f_group_concat("select distinct
convert(varchar(100), child_id)
from group_c
where parent_id = " + convert(varchar, parent_id) + "
order by child_id"
, default
)
,20) as child_id_list
from group_c
group by parent_id
order by parent_id
/*
results should look like:
parent_id child_id_list
----------- --------------------
1 1,2,3,4
2 1,4,6
3 1,2
4 1
5 0
*/
go
-----
print "
******************************
QUERY # 1 : List of parents and associated children (separator = ' - ')
******************************
"
select parent_id,
-- we know child_id_list is relatively narrow in this case
-- so reduce the width of the output via left(--,20)
left( dbo.sp_f_group_concat("select distinct
convert(varchar(100), child_id)
from group_c
where parent_id = " + convert(varchar, parent_id) + "
order by child_id"
, " - "
)
,20) as child_id_list
from group_c
group by parent_id
order by parent_id
/*
results should look like:
parent_id child_id_list
----------- --------------------
1 1 - 2 - 3 - 4
2 1 - 4 - 6
3 1 - 2
4 1
5 0
*/
go
++++++++++++++++++++++++++++++++++++++++++++++++
use tempdb
go
set nocount on
go
/*
reproduction of the MySQL/GROUP_CONCAT() examples from:
https://www.percona.com/blog/2013/10/22/the-power-of-mysql-group_concat/
Assumptions
===========
- ASE's identity column attribute generates the same results as
MySQL's AUTO_INCREMENT column attribute
- otherwise the auto-generated customer.id values won't
match the manually entered issues.company_id values
- otherwise the auto-generated engineers.id and issues.id
values won't match the manually entered values for
workflow's engineer_id/issue_id pairs
*/
if object_id('engineers') is not NULL
drop table engineers
go
create table engineers
(id smallint identity
,e_name varchar(30) not NULL
,e_surname varchar(30) not NULL
,url varchar(255) not NULL
)
go
alter table engineers
add primary key (id)
go
insert engineers (e_name, e_surname, url) values ('Miguel', 'Nieto', 'https://www.percona.com/about-us/our-team/miguel-angel-nieto')
insert engineers (e_name, e_surname, url) values ('Marcos', 'Albe', 'https://www.percona.com/about-us/our-team/marcos-albe')
insert engineers (e_name, e_surname, url) values ('Valerii', 'Kravchuk', 'https://www.percona.com/about-us/our-team/valerii-kravchuk')
insert engineers (e_name, e_surname, url) values ('Michael', 'Rikmas', 'https://www.percona.com/about-us/our-team/michael-rikmas')
go
if object_id('customers') is not NULL
drop table customers
go
create table customers
(id smallint identity
,company_name varchar(30) not NULL
,url varchar(255) not NULL
)
go
alter table customers
add primary key (id)
go
insert customers (company_name, url) values ('OT','http://www.ovaistariq.net/')
insert customers (company_name, url) values ('PZ','http://www.peterzaitsev.com/')
insert customers (company_name, url) values ('VK','http://mysqlentomologist.blogspot.com/')
insert customers (company_name, url) values ('FD','http://www.lefred.be/')
insert customers (company_name, url) values ('AS','http://mysqlunlimited.blogspot.com/')
insert customers (company_name, url) values ('SS','https://www.flamingspork.com/blog/')
go
if object_id('issues') is not NULL
drop table issues
go
create table issues
(id smallint identity
,customer_id smallint not NULL
,description text
)
go
alter table issues
add primary key (id)
go
insert issues (customer_id, description) values (1,'Fix replication')
insert issues (customer_id, description) values (2,'Help with installation of Percona Cluster')
insert issues (customer_id, description) values (3,'Hardware suggestions')
insert issues (customer_id, description) values (4,'Error: no space left')
insert issues (customer_id, description) values (5,'Help with setup daily backup by Xtrabackup')
insert issues (customer_id, description) values (6,'Poke sales about Support agreement renewal')
insert issues (customer_id, description) values (4,'Add more accounts for customer')
insert issues (customer_id, description) values (2,'Create Hot Fix of Bug 1040735')
insert issues (customer_id, description) values (1,'Query optimisation')
insert issues (customer_id, description) values (1,'Prepare custom build for Solaris')
insert issues (customer_id, description) values (2,'explain about Percona Monitoring plugins')
insert issues (customer_id, description) values (6,'Prepare access for customer servers for future work')
insert issues (customer_id, description) values (5,'Decribe load balancing for pt-online-schema-change')
insert issues (customer_id, description) values (4,'Managing deadlocks')
insert issues (customer_id, description) values (1,'Suggestions about buffer pool size')
go
if object_id('workflow') is not NULL
drop table workflow
go
create table workflow
(action_id int identity
,engineer_id smallint not NULL
,issue_id smallint not NULL
)
go
alter table workflow
add primary key (action_id)
go
insert workflow (engineer_id, issue_id) values (1,1)
insert workflow (engineer_id, issue_id) values (4,2)
insert workflow (engineer_id, issue_id) values (2,3)
insert workflow (engineer_id, issue_id) values (1,4)
insert workflow (engineer_id, issue_id) values (3,5)
insert workflow (engineer_id, issue_id) values (2,6)
insert workflow (engineer_id, issue_id) values (3,7)
insert workflow (engineer_id, issue_id) values (2,8)
insert workflow (engineer_id, issue_id) values (2,9)
insert workflow (engineer_id, issue_id) values (1,10)
insert workflow (engineer_id, issue_id) values (3,11)
insert workflow (engineer_id, issue_id) values (2,12)
insert workflow (engineer_id, issue_id) values (2,13)
insert workflow (engineer_id, issue_id) values (3,14)
insert workflow (engineer_id, issue_id) values (1,15)
insert workflow (engineer_id, issue_id) values (1,9)
insert workflow (engineer_id, issue_id) values (4,14)
insert workflow (engineer_id, issue_id) values (2,9)
insert workflow (engineer_id, issue_id) values (1,15)
insert workflow (engineer_id, issue_id) values (3,10)
insert workflow (engineer_id, issue_id) values (4,2)
insert workflow (engineer_id, issue_id) values (2,15)
insert workflow (engineer_id, issue_id) values (4,8)
insert workflow (engineer_id, issue_id) values (4,4)
insert workflow (engineer_id, issue_id) values (3,11)
insert workflow (engineer_id, issue_id) values (1,7)
insert workflow (engineer_id, issue_id) values (3,7)
insert workflow (engineer_id, issue_id) values (1,1)
insert workflow (engineer_id, issue_id) values (1,9)
insert workflow (engineer_id, issue_id) values (3,4)
insert workflow (engineer_id, issue_id) values (4,3)
insert workflow (engineer_id, issue_id) values (1,5)
insert workflow (engineer_id, issue_id) values (1,7)
insert workflow (engineer_id, issue_id) values (1,4)
insert workflow (engineer_id, issue_id) values (2,4)
insert workflow (engineer_id, issue_id) values (2,5)
go
print "
******************************
QUERY # 1 : List of issues for each engineer
******************************
"
/*
for display purposes we'll use left() to reduce column widths based on known max widths for the test data
*/
select left(e.e_name + ' ' + e.e_surname, 20) as engineer,
left(dbo.sp_f_group_concat("select distinct
convert(varchar,w.issue_id) + ' (' + c.company_name + ')'
from workflow w,
engineers e,
customers c,
issues i
where w.engineer_id = e.id
and w.issue_id = i.id
and i.customer_id = c.id
and e.id = " + convert(varchar,e.id) + "
order by w.issue_id"
, ', ')
, 80) as 'issue (customer)'
from workflow w,
engineers e,
customers c,
issues i
where w.engineer_id = e.id
and w.issue_id = i.id
and i.customer_id = c.id
group by e.id
order by e_name, e_surname
/*
results should look like:
engineer issue (customer)
------------------------------ --------------------------------------------------------------------------------
Marcos Albe 3 (VK), 4 (FD), 5 (AS), 6 (SS), 8 (PZ), 9 (OT), 12 (SS), 13 (AS), 15 (OT)
Michael Rikmas 2 (PZ), 3 (VK), 4 (FD), 8 (PZ), 14 (FD)
Miguel Nieto 1 (OT), 4 (FD), 5 (AS), 7 (FD), 9 (OT), 10 (OT), 15 (OT)
Valerii Kravchuk 4 (FD), 5 (AS), 7 (FD), 10 (OT), 11 (PZ), 14 (FD)
*/
go
print "
******************************
QUERY # 2 : List of engineers for each customer (nested group_concat() calls)
******************************
"
/*
while technically possible to nest our sp_f_group_concat() calls, the outer
call becomes unwieldly since it will have to duplicate a copy of the inner
call (and the full text for the e_list derived table) for each company;
reason being that the e_list derived table has to be re-created for each
outer call (per company)
to make the code easier to read we're going to materialize the e_list derived table
as a #temp table; for large data sets we'd want to look at the feasibilty of
adding an index for performance reasons
for display purposes we'll use left() to reduce column widths based on known
max widths for the test data
*/
-- build/populate the #e_list table with a set of issue id's and associated engineer lists
if object_id('#e_list') is not NULL
drop table #e_list
go
create table #e_list
(i_id int
,engineer_list varchar(1000)
)
go
insert #e_list
select i.id as i_id,
dbo.sp_f_group_concat("select distinct
e.e_name + ' ' + e.e_surname
from workflow w,
engineers e,
issues i
where w.engineer_id = e.id
and w.issue_id = i.id
and i.id = " + convert(varchar, i.id) + "
order by e.e_name, e.e_surname"
, ', ') as engineer_list
from workflow w,
engineers e,
issues i
where w.engineer_id = e.id
and w.issue_id = i.id
group by i.id
go
-- now run the main query to display isuses/engineer-lists by company
select left(c.company_name, 10) as company,
left(dbo.sp_f_group_concat("select distinct
convert(varchar,e_list.i_id) + ' (' + e_list.engineer_list + ')'
from workflow w,
engineers e,
customers c,
issues i,
#e_list e_list
where w.engineer_id = e.id
and w.issue_id = i.id
and i.customer_id = c.id
and w.issue_id = e_list.i_id
and c.id = " + convert(varchar, c.id) + "
order by w.issue_id"
, ', ' )
, 140) as issue
from workflow w,
engineers e,
customers c,
issues i,
#e_list e_list
where w.engineer_id = e.id
and w.issue_id = i.id
and i.customer_id = c.id
and w.issue_id = e_list.i_id
group by c.id
order by c.company_name
/*
results should look like:
company issue
---------- --------------------------------------------------------------------------------------------------------------------------------------------
AS 5 (Marcos Albe, Miguel Nieto, Valerii Kravchuk), 13 (Marcos Albe)
FD 4 (Marcos Albe, Michael Rikmas, Miguel Nieto, Valerii Kravchuk), 7 (Miguel Nieto, Valerii Kravchuk), 14 (Michael Rikmas, Valerii Kravchuk)
OT 1 (Miguel Nieto), 9 (Marcos Albe, Miguel Nieto), 10 (Miguel Nieto, Valerii Kravchuk), 15 (Marcos Albe, Miguel Nieto)
PZ 2 (Michael Rikmas), 8 (Marcos Albe, Michael Rikmas), 11 (Valerii Kravchuk)
SS 6 (Marcos Albe), 12 (Marcos Albe)
VK 3 (Marcos Albe, Michael Rikmas)
*/
go
++++++++++++++
注意:对于示例查询,您会注意到传递给sp_f_group_concat()函数的@sql字符串基本上是父查询的副本加上其他&#39;其中& #39;允许将查询限制为与父查询匹配的行的子句&#39; group by&#39;子句(即附加的&#39; where&#39;子句与&#39;组中的列匹配的条款)