admin管理员组

文章数量:1128330

I have logic replacing characters with '':

DECLARE
  v_owner VARCHAR2(50) := 'LFS_SURVEY'; -- Replace with your schema/owner name
  v_table_name VARCHAR2(50) := 'LFS_FAMILY_TAB'; -- Replace with your table name
  v_rp_ids VARCHAR2(100) := '2030, 2031'; -- Replace with your RP_ID values
  v_columns CLOB;
  v_query   CLOB;
BEGIN
  -- Get all column names with transformations based on data type
EXECUTE IMMEDIATE 'SELECT RTRIM(XMLAGG(XMLELEMENT(e, 
    CASE 
      WHEN COLUMN_NAME = ''F_M_ID'' THEN ''TO_CHAR(t.F_M_ID) AS F_M_ID''
      WHEN DATA_TYPE LIKE ''%CHAR%'' THEN ''REPLACE(REPLACE(t.'' || COLUMN_NAME || '', CHR(13), ''''), CHR(10), '''') AS '' || COLUMN_NAME
      ELSE ''t.'' || COLUMN_NAME 
    END || '', '').EXTRACT(''//text()'') ORDER BY COLUMN_ID).GETCLOBVAL(), '', '') 
    FROM ALL_TAB_COLUMNS 
    WHERE TABLE_NAME = ''' || v_table_name || ''' 
    AND OWNER = ''' || v_owner || '''' 
    INTO v_columns;
  DBMS_OUTPUT.PUT_LINE(v_columns);
END;
/

The output shows ' instead of '', e.g.:

REPLACE(REPLACE(t.Q_851, CHR(13), &appos;), CHR(10), &appos;) AS Q_851

How can I force it to print ''?

I have logic replacing characters with '':

DECLARE
  v_owner VARCHAR2(50) := 'LFS_SURVEY'; -- Replace with your schema/owner name
  v_table_name VARCHAR2(50) := 'LFS_FAMILY_TAB'; -- Replace with your table name
  v_rp_ids VARCHAR2(100) := '2030, 2031'; -- Replace with your RP_ID values
  v_columns CLOB;
  v_query   CLOB;
BEGIN
  -- Get all column names with transformations based on data type
EXECUTE IMMEDIATE 'SELECT RTRIM(XMLAGG(XMLELEMENT(e, 
    CASE 
      WHEN COLUMN_NAME = ''F_M_ID'' THEN ''TO_CHAR(t.F_M_ID) AS F_M_ID''
      WHEN DATA_TYPE LIKE ''%CHAR%'' THEN ''REPLACE(REPLACE(t.'' || COLUMN_NAME || '', CHR(13), ''''), CHR(10), '''') AS '' || COLUMN_NAME
      ELSE ''t.'' || COLUMN_NAME 
    END || '', '').EXTRACT(''//text()'') ORDER BY COLUMN_ID).GETCLOBVAL(), '', '') 
    FROM ALL_TAB_COLUMNS 
    WHERE TABLE_NAME = ''' || v_table_name || ''' 
    AND OWNER = ''' || v_owner || '''' 
    INTO v_columns;
  DBMS_OUTPUT.PUT_LINE(v_columns);
END;
/

The output shows ' instead of '', e.g.:

REPLACE(REPLACE(t.Q_851, CHR(13), &appos;), CHR(10), &appos;) AS Q_851

How can I force it to print ''?

Share Improve this question edited Jan 8 at 17:45 Alex Poole 191k11 gold badges192 silver badges337 bronze badges asked Jan 8 at 14:25 rayray 497 bronze badges
Add a comment  | 

3 Answers 3

Reset to default 2

As an empty string '' is equivalent to null in Oracle, in this instance you could avoid the issue by replacing tab/newline with null instead; so rather than this:

WHEN DATA_TYPE LIKE ''%CHAR%''
THEN ''REPLACE(REPLACE(t.'' || COLUMN_NAME || '', CHR(13), ''''), CHR(10), '''') AS '' || COLUMN_NAME

do this:

WHEN DATA_TYPE LIKE ''%CHAR%''
THEN ''REPLACE(REPLACE(t.'' || COLUMN_NAME || '', CHR(13), null), CHR(10), null) AS '' || COLUMN_NAME

You don't need to use dynamic SQL here though, as you can statically query the data dictionary - the target schema/table name are variables here, not identifiers:

DECLARE
  v_owner VARCHAR2(50) := 'LFS_SURVEY'; -- Replace with your schema/owner name
  v_table_name VARCHAR2(50) := 'LFS_FAMILY_TAB'; -- Replace with your table name
  v_rp_ids VARCHAR2(100) := '2030, 2031'; -- Replace with your RP_ID values
  v_columns CLOB;
  v_query   CLOB;
BEGIN
  -- Get all column names with transformations based on data type
  SELECT RTRIM(XMLAGG(XMLELEMENT(e, 
    CASE 
      WHEN COLUMN_NAME = 'F_M_ID'
      THEN 'TO_CHAR(t.F_M_ID) AS F_M_ID'
      WHEN DATA_TYPE LIKE '%CHAR%'
      THEN 'REPLACE(REPLACE(t.' || COLUMN_NAME || ', CHR(13), null), CHR(10), null) AS ' || COLUMN_NAME
      ELSE 't.' || COLUMN_NAME 
    END || ', ').EXTRACT('//text()') ORDER BY COLUMN_ID).GETCLOBVAL(), ', ') 
  INTO v_columns
  FROM ALL_TAB_COLUMNS 
  WHERE TABLE_NAME = v_table_name
  AND OWNER = v_owner;
  DBMS_OUTPUT.PUT_LINE(v_columns);
END;
/

With a dummy table created as:

create table LFS_FAMILY_TAB (f_m_id number, foo date, bar varchar2(30))

that outputs:

TO_CHAR(t.F_M_ID) AS F_M_ID, t.FOO, REPLACE(REPLACE(t.BAR, CHR(13), null), CHR(10), null) AS BAR

fiddle

And that is functionally the same as

TO_CHAR(t.F_M_ID) AS F_M_ID, t.FOO, REPLACE(REPLACE(t.BAR, CHR(13), ''), CHR(10), '') AS BAR

If you really want '' instead of null (or perhaps have to deal with other text literals) then you can use XMLTable, which doesn't escape the entities like extract (and XMLQuery):

DECLARE
  v_owner VARCHAR2(50) := 'LFS_SURVEY'; -- Replace with your schema/owner name
  v_table_name VARCHAR2(50) := 'LFS_FAMILY_TAB'; -- Replace with your table name
  v_rp_ids VARCHAR2(100) := '2030, 2031'; -- Replace with your RP_ID values
  v_columns CLOB;
  v_query   CLOB;
BEGIN
  -- Get all column names with transformations based on data type
  SELECT RTRIM(x.str, ', ')
  INTO v_columns
  FROM (
    SELECT XMLELEMENT(r, XMLAGG(XMLELEMENT(e, 
      CASE 
        WHEN COLUMN_NAME = 'F_M_ID'
        THEN 'TO_CHAR(t.F_M_ID) AS F_M_ID'
        WHEN DATA_TYPE LIKE '%CHAR%'
        THEN 'REPLACE(REPLACE(t.' || COLUMN_NAME || ', CHR(13), ''''), CHR(10), '''') AS ' || COLUMN_NAME
        ELSE 't.' || COLUMN_NAME 
      END || ', ') ORDER BY COLUMN_ID)) AS xmlx
    FROM ALL_TAB_COLUMNS
    WHERE TABLE_NAME = v_table_name
    AND OWNER = v_owner
  ) atc
  CROSS JOIN XMLTABLE(
    '/R'
    PASSING atc.xmlx
    COLUMNS str CLOB PATH '.'
  ) x;
  DBMS_OUTPUT.PUT_LINE(v_columns);
END;
/

which for the same dummy table generates:

TO_CHAR(t.F_M_ID) AS F_M_ID, t.FOO, REPLACE(REPLACE(t.BAR, CHR(13), ''), CHR(10), '') AS BAR

fiddle


Incidentally, your original code produces ', but that's just one entity-escaped quote '. If you wanted two '' then you need even more in your code:

WHEN DATA_TYPE LIKE ''%CHAR%''
THEN ''REPLACE(REPLACE(t.'' || COLUMN_NAME || '', CHR(13), ''''''''), CHR(10), '''''''') AS '' || COLUMN_NAME

But as that would then produce '' it wouldn't actually solve your issue - though you then manually unescape those entities.

Oracle represents 4 consecutive single quotes as one single quote. So, maybe the most readable way when building some dynamic sql would be to declare a single quote variable
sq VARCHAR2(1) := '''';
like below:

DECLARE
  v_table_name VARCHAR2(50) := 'LFS_FAMILY_TAB'; 
  v_query   CLOB;
  sq   VARCHAR2(1) := '''';  -- 4 single quotes are represented as one single quote
BEGIN
  v_query := 'SELECT '|| Chr(10) || 
             '   CASE WHEN COLUMN_NAME = ' || sq || sq || 'F_M_ID' || sq || sq || Chr(10) ||
             '       THEN ' || sq || sq || 'TO_CHAR(t.F_M_ID)' || sq || sq || ' AS F_M_ID ' || Chr(10) ||
             '   ELSE t.' || sq || 'COLUMN_NAME ' || sq || Chr(10) || 
             '   END as SOME_ALIAS ' || Chr(10) || 
             'FROM ' || v_table_name || Chr(10) ||
             '      ... ';
  dbms_output.put_line(v_query);           
END;
/
--  R e s u l t : 
SELECT 
   CASE WHEN COLUMN_NAME = ''F_M_ID''
       THEN ''TO_CHAR(t.F_M_ID)'' AS F_M_ID 
   ELSE t.'COLUMN_NAME '
   END as SOME_ALIAS 
FROM LFS_FAMILY_TAB
      ... 

NOTE:
This way you can insert sq (or two, three of them) anywhere in the string and keep the code readable and maintainable...

Why 4 to get 1 ?

--  Some text enclosed within quotes and containing a single quote like 
--  "father's house"
--  requires duplication of single quote to result as one single quote
Select '... father''s house ...' as SAMPLE From Dual;
SAMPLE
... father's house ...
-- if you remove all other text but the single quotes
-- you will end up with 2 outer enclosing single quotes and 2 inner ones resulting as just one:
Select '''' as SAMPLE From Dual;
SAMPLE
'

fiddle

There are a couple of pl/sql techniques that will simplify your code and reduce the chances of running into issues like yours. As others have indicated, the EXECUTE IMMEDIATE is not needed in this particular case but if you choose to do so anyway, here are some tips.

alternative quoting aka Q-quote syntax

This allows you to use another string delimiter than single quote - thus eliminating the need for escaping quotes (docs)

Example:

BEGIN
    -- the "old way"
    dbms_output.put_line('this is a quote: '', and here are 2 quotes: ''''');
    -- alternative quoting
    dbms_output.put_line(q'!this is a quote: ', and here are 2 quotes: ''!');
END;
/  
this is a quote: ', and here are 2 quotes: ''
this is a quote: ', and here are 2 quotes: ''


PL/SQL procedure successfully completed.

use bind variables not concatenation

Contatenating variable values in pl/sql and sql is not a good practice. It makes the code vulnerable to sql injection. Moreover it's hard to read and a nightmare to maintain (as you showed). Try to use bind variables wherever possible. It's better coding and a lot more readable.

Example:

DECLARE
  l_ename emp.ename%TYPE := 'KING';
  l_statement VARCHAR2(4000);
  l_result  VARCHAR2(4000);
BEGIN
   -- without bind variables
   l_statement := q'!
SELECT ename ||' earns '|| sal
  FROM emp where ename = !' || '''KING''';
    EXECUTE IMMEDIATE l_statement INTO l_result;
  dbms_output.put_line(l_result);

   -- with bind variables
   l_statement := q'!
SELECT ename ||' earns '|| sal
  FROM emp where ename = :1!';
    EXECUTE IMMEDIATE l_statement INTO l_result USING l_ename;
  dbms_output.put_line(l_result);
END;
/
KING earns 5000
KING earns 5000

本文标签: sqlHow to print 3939 instead of ampaposStack Overflow