使用动态 sql 为 select 语句创建列

Posted

技术标签:

【中文标题】使用动态 sql 为 select 语句创建列【英文标题】:using dynamic sql to create column for select statement 【发布时间】:2018-02-12 11:19:29 【问题描述】:

我正在为分页结果编写一个存储过程,这个结果可以按某些值排序。我在 select 语句中确实有一个 switch case,但是因为它试图在 rownum 上执行 orderby,所以速度很慢。

现在我正在尝试使用动态 sql 在 select 之外构建查询,但我不知道我正在做的事情是否可行。

这是我在 Oracle SQL Developer 中的 SQL:

create or replace PROCEDURE Sp_tsa_trainees_pagination (
schemeid IN INT,
searchval IN VARCHAR2,
pagesize IN INT DEFAULT 20,
currentpage IN INT DEFAULT 1,
--orderby IN VARCHAR2,
cursor_  OUT SYS_REFCURSOR)

AS
-- LOCAL VARIABLES
totalcount INT;
numberofpages INT;
startposition NUMBER;
endposition NUMBER;
orderby VARCHAR2(100) := 'surname asc' ;
dynamic_query VARCHAR(255) := 'row_number() over (order by t.SURNAME DESC, t.FORENAMES DESC) AS rnum';
BEGIN

-- Get total number of trainees in scheme
select COUNT(t.ORG_REGISTRATION_ID) 
into totalcount FROM v_trainee t 
where t.ORG_REGISTRATION_ID = schemeid 
AND t.status = 'A' and LOWER(t.trainee_name) like '%' || LOWER(searchval) || '%';

  -- calculate number of pages in the pagination by dividing total number of records by how many to display for each page
  numberofpages := totalcount / pagesize;

  -- get start position by multiplying number of records to display for each page by current page
  startposition := pagesize *( currentpage-1);

  -- add calculated start position by number of records to display to get end position
  endposition := startposition + pagesize;

  CASE orderby 
    WHEN 'surname desc' THEN dynamic_query := 'row_number() over (order by t.SURNAME DESC, t.FORENAMES DESC) AS rnum';
    WHEN 'surname asc' THEN dynamic_query := 'row_number() over (order by t.SURNAME ASC, t.FORENAMES ASC) AS rnum';
END CASE;


    OPEN cursor_ FOR
    Select * from 
(
SELECT 
-- order by based on selection

dynamic_query rnum,

t.ORG_REGISTRATION_ID SearchId,
    t.FORENAMES Forenames,
    t.FORENAME Forename,
    t.SURNAME Surname,
    t.person_id PersonId,
    t.trainee_name TraineeName,
    t.STATUS Status,
    t.IPD_ANNUAL_REVIEW_DATE AnnualReviewDate,
    t.ANNUAL_REVIEW_STATUS AnnualReviewStatus,
    t.payment_received PaymentRecieved,
    t.TRAINEE_ID TraineeId,
    t.IPD_SIGNUP_DATE IpdSignupDate,
    t.START_DATE StartDate,
    t.END_DATE EndDate,
    t.LENGTH_ON_SCHEME LengthOnScheme,
    t.EMPLOYEE_NUMBER EmploymentNumber,
    t.SELECTED_LEVEL SelectedLevel,
    t.SELECTED_LEVEL_DESCRIPTION SelectedLevelDescription,
    t.ELIGIBLE_LEVEL EligibleLevel,
    t.ELIGIBLE_LEVEL_DESCRIPTION EligibleLevelDescription,
    sce.FORENAMES SceForenames,
    sce.FORENAME SceForename,
    sce.SURNAME SceSurname,
    sce.mentor_name SceName,
    sce.EMPLOYEE_NUMBER SceEmployeeNumber,
    de.FORENAMES DeForenames,
    de.FORENAME DeForename,
    de.SURNAME DeSurname,
    de.mentor_name DeName,
    de.EMPLOYEE_NUMBER DeEmployeeNumber,
    t.COMPLETED_ATTRIBUTE_LEVELS CompletedAttributeLevels,
    t.ATTRIBUTE_LEVEL_COUNT AttributeLevelCount,

    -- get percentage
    CASE t.ATTRIBUTE_LEVEL_COUNT
    WHEN 0 THEN 0
    ELSE
    COMPLETED_ATTRIBUTE_LEVELS / t.ATTRIBUTE_LEVEL_COUNT * 100
    END percentage,

    DECODE(F_ISTRAINEEGROUPMEMBER(t.ORG_REGISTRATION_ID, 'S', t.person_id),'Y','N','Y') WithoutTsaGroup,
    orr.status SchemeStatus,
    (select count(*) from TRAINING_GROUP_TRAINEE tgt where tgt.trainee_id = t.TRAINEE_ID) NUMBER_OF_GROUPS,
    TotalCount
  FROM v_trainee t
  INNER JOIN org_registration orr ON t.ORG_REGISTRATION_ID = orr.id
  LEFT OUTER JOIN v_mentor sce    ON t.sce_id = sce.MENTOR_ID
  LEFT OUTER JOIN v_mentor de     ON t.de_id = de.MENTOR_ID
  where t.ORG_REGISTRATION_ID = schemeid  AND t.status = 'A' 
  and LOWER(t.trainee_name) like '%' || LOWER(searchval) || '%'
  )
  where rnum >= startposition and rnum <= endposition;

END;

我想将此变量与分配的 sql 一起使用:

dynamic_query rnum,

但是当我执行存储过程时,我得到了这个错误:

ORA-01722:无效号码 ORA-06512:在 “db.SP_TSA_TRAINEES_PAGINATION”,第 46 行 ORA-06512:第 13 行

所以基本上我的问题是我可以将 SQL 分配给 VARCHAR2,然后在 select 语句中动态使用它。

【问题讨论】:

【参考方案1】:

为此,您可能需要动态 SQL。例如:

create or replace procedure testDyn(n in number, C OUT SYS_REFCURSOR) is
    vDynamicPart varchar2(1000);
    vSQl         varchar2(1000);
begin
    --
    if  (n = 1) then
        vDynamicPart := 'count(1)';
    else
        vDynamicPart := 'count(null)';
    end if;
    --
    vSQl := 'select ' || vDynamicPart || ' from dual';
    open C for vSQl;
end;

如果你叫它

declare
    n1 number;
    n2 number;
    C1 SYS_REFCURSOR;
    C2 SYS_REFCURSOR;
begin
    testDyn(1, C1);
    testDyn(2, C2);

    fetch C1 into n1;
    fetch C2 into n2;

    dbms_output.put_line('n1: ' || n1);
    dbms_output.put_line('n2: ' || n2);
end;

你得到:

n1: 1
n2: 0

【讨论】:

以上是关于使用动态 sql 为 select 语句创建列的主要内容,如果未能解决你的问题,请参考以下文章

在access利用SQL语句中如何创建表?

如何为动态生成 Select 语句的 30 列表创建索引

动态执行SQL语句,拼接字符串,select中带有一个变量

SQL语句中,子句不能使用列别名问题

用于计算运行平均列的 SQL Select 语句

SELECT 语句中的列别名不适用于 SQuirrel SQL + Firebird