Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/85.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_Database_Sybase - Fatal编程技术网

为多行创建连接SQL查询

为多行创建连接SQL查询,sql,database,sybase,Sql,Database,Sybase,我们正在尝试在Sybase中创建一个SQL查询,在这里我们可以将多行连接到一个选定行中 布局: | Type | Skill | ---------------- | A | 1 | A | 2 | B | 1 ETC 我希望输出类似于:A(1,2)这是哪种特定类型的Sybase数据库?如果是ASE,那么您必须使用循环方法(例如游标),或者使用局部变量对UPDATE语句使用某种奇特的技巧(这里太多,无法快速描述,但详细信息在我的书《sybase AS

我们正在尝试在Sybase中创建一个SQL查询,在这里我们可以将多行连接到一个选定行中

布局:

| Type | Skill |
----------------
| A    |  1     
| A    |  2     
| B    |  1
ETC

我希望输出类似于:A(1,2)

这是哪种特定类型的Sybase数据库?如果是ASE,那么您必须使用循环方法(例如游标),或者使用局部变量对UPDATE语句使用某种奇特的技巧(这里太多,无法快速描述,但详细信息在我的书《sybase ASE的提示、技巧和配方》中)

如果使用ASE 16,您可以使用自己的函数来模拟列表()函数;请参见此线程中的我的回答:

请注意Edison关于KBA2260479的回答……在事务中使用表变量时出现了一个错误

-----------根据从sap.com链接复制相关信息的建议

引用一个例子@(对不起,我也不打算复制这个链接;附在本文后面的例子显示了匹配percona例子的结果)

为了在ASE中模拟MySQL的GROUP_CONCAT()函数,我们需要了解一些问题/限制/观察结果:

  • ASE不允许聚合/UDF(例如,将结果集作为参数传递给函数)

  • ASE不允许将表变量定义为输入参数的UDF

  • ASE不允许在UDF中创建临时表

  • ASE 16 SP02+允许在UDF内创建/使用表变量

  • ASE确实允许在UDF中使用exec()构造

  • GROUP_CONCAT()参数由要追加的列/字符串、一个“order by”子句和一个可选分隔符组成(请参见上面percona.com链接中的示例);不难看出列/字符串+order by子句是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包含任何必要的“group/order by”子句(即,UDF不会执行任何排序操作)

2-UDF创建一个表变量,其中定义了一列来保存@sql查询的结果

2a-表变量的列必须在数据类型方面与@sql查询的选择/投影列表相匹配

3-UDF通过exec()构造填充表变量:

    exec("insert @table_var " + @sql)
4-UDF使用游标循环表变量中的记录

4a-游标没有“order by”子句=>假设在插入到表变量时,行是基于@sql排序的


有关此特定UDF的一些详细信息:

1-我在sybsystemprocs数据库中创建了UDF,并将其命名为:

 sp_f_group_concat
1a.“sp_u2;”前缀表示可以从任何数据库中执行UDF

1b-通过“f_2;”字符串,我可以快速/直观地看到这是一个函数,而不是一个系统存储过程

2-创建UDF时假设传入的SQL/SELECT查询将有一个由单个varchar(100)列组成的SELECT/projection列表

2a-调用过程将需要执行任何必要的数据类型转换(到char)和列/字符串的任何串联

2b-@sql输入参数已定义为varchar(1000),并且@separator已定义为varchar(10),默认值为单个逗号(','))

2c-UDF的所有者需要根据他们希望在环境中处理的内容修改varchar()长度

3-由于UDF无法确保TF:7703(允许按行将数据累积到单个@变量中)已启用,并且UDF不执行任何排序,因此我们将使用光标逐步遍历表变量中的记录

4-从percona.com的示例中看不出MySQL的GROUP_CONCAT()函数如何处理将NULL附加为非NULL值(例如,是否忽略NULL?是否将NULL转换为空字符串“”?是否将NULL转换为字符串“NULL”?);最终结果是,如果UDF所有者/用户发现NULL未按预期处理,则可能需要重新访问UDF和/或@sql设计


嗯,不能将文件附加到stackoverflow帖子上吗?好的,剪切粘贴它是…恶心,不是源代码看起来的样子

++++++++++++++sp_f_group_concat.sql

  • UDF-DDL

++++++++++++++sp_f_group_concat.test1.sql

  • percona.com group_c表格示例
  • 第一个查询使用默认分隔符(单个逗号)显示
  • 第二个查询显示使用3字符分隔符

++++++++++++++sp_f_group_concat.test2.sql

  • percona.com使用工程师/客户/问题/工作流表的示例
  • 第二个查询(嵌套组_CONCAT()调用)使用临时表存储中间结果,因为单个查询解决方案过于复杂

++++++++++++++

注意:对于示例查询,您会注意到传递给sp_f_group_concat()函数的@sql字符串基本上是父查询的副本,外加一个额外的“where”子句,以允许将查询限制为仅与父查询的“groupby”子句匹配的行(即,额外的“where”子句与列匹配)在“group by”子句中
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