I have a query where I need to remove the first and last quote from a string to use it in clause. When I run the following query ::
with t as (
select '1,2,3' x from dual)
select translate(x, ' '||chr(39)||chr(34), ' ' ) from t
it gives the result > 1,2,3
But when I run the following query ::
select * from care_topic_templates where care_topic_id in (
with t as (
select '1,2,3' x from dual)
select translate(x, ' '||chr(39)||chr(34), ' ' ) from t
);
it gives this error > ORA-01722: invalid number.
Because you are comparing an integer id to a string, which looks like '1,2,3' -- and this string cannot be converted to an integer, even after the strange substitutions using translate(). Strings are not lists.
You can do what you want using like and a correlated subquery:
select *
from care_topic_templates
where exists (select 1
from (select '1,2,3' as x from dual) x
where ',' || x || ',' like '%,' || care_topic_id || ',%'
);
Or, in your case:
select *
from care_topic_templates
where exists (select 1
from (select '1,2,3' as x from dual) x
where ',' || translate(x, ' '||chr(39)||chr(34), ' ') || ',' like '%,' || care_topic_id || ',%'
);
This is following the logic of your query. There are other ways to express this logic.
Related
How to convert LISTAGG with case statements to XMLAGG equivalent, so as to avoid the concatenation error.
#ECHO ${cols_2 ||32767||varchar2}$ --Declare variable
SELECT LISTAGG( 'MAX(CASE WHEN CATEGORY = '''||CATEGORY||''' THEN "'||"LEVEL"||'" END) AS "'||"LEVEL"||'_'||CATEGORY||'"' , ',' )
WITHIN GROUP( ORDER BY CATEGORY, "LEVEL" DESC )
INTO cols_2
FROM (
SELECT DISTINCT "LEVEL", CATEGORY
FROM temp
);
I tried this and I'm getting an error saying missing keyword
#ECHO ${cols_2 ||32767||varchar2}$ --Declare variable
select rtrim (
xmlagg (xmlelement (e, 'MAX(CASE WHEN CATEGORY = '''||CATEGORY||''' THEN "'||"LEVEL"||'" END) AS "'||LEVEL||'_'||CATEGORY||'"', ',') order by 1,2 desc).extract (
'//text()'),
', ')
INTO cols_2
FROM (
SELECT DISTINCT "LEVEL", CATEGORY
temp
);
I have tried this an declared the cols_2 as clob type :-
SELECT DBMS_XMLGEN.CONVERT (
RTRIM (
XMLAGG (XMLELEMENT (
e,
'MAX(CASE WHEN CATEGORY = '''
|| CATEGORY
|| ''' THEN "'
|| "LEVEL"
|| '" END) AS "'
|| "LEVEL"
|| '_'
|| CATEGORY
|| '"',
',')
ORDER BY 1, DESC).EXTRACT('//text()').getclobval(),','),1)
', '),
1)
INTO cols_2
FROM (SELECT DISTINCT "LEVEL", CATEGORY
FROM temp);
Yet my issue is not resolved ,Im getting an error while trying to execute it as a procedure like :-
Error in concatenation of `LISTAGG` function[Not a duplicate question]
You are getting the missing keyword error because you are most likely attempting to run the second query as a standalone query instead of in a PL/SQL block. When you are doing that, you have to remove your into cols_2 clause. That is your immediate issue that should resolve your error.
Also, based on your prior question, using the XML functions will escape your ' and " characters so you will want to make sure to unescape them back to their original characters so you can use them in your dynamic sql query like this:
SELECT DBMS_XMLGEN.CONVERT (
RTRIM (
XMLAGG (XMLELEMENT (
e,
'MAX(CASE WHEN CATEGORY = '''
|| CATEGORY
|| ''' THEN "'
|| "LEVEL"
|| '" END) AS "'
|| "LEVEL"
|| '_'
|| CATEGORY
|| '"',
',')
ORDER BY 1, 2 DESC).EXTRACT ('//text()'),
', '),
1)
--INTO cols_2
FROM (SELECT DISTINCT "LEVEL", CATEGORY
FROM temp);
I want to make an array and put into it two id's, but I got a mistake:
array value must start with “{” or dimension information
ids_list character varying[] := ' || (SELECT COALESCE(quote_literal((array_agg(DISTINCT house_guid)) || ''',''' || quote_literal(array_agg(DISTINCT guid))), 'NULL') FROM tb) || ';
use array_agg function
with t1 as
(
select * from
(
select 'test_SQL_01' as ID
union
select 'test_SQL_02_PQR_01'
union
select 'test_SQL_03_055'
union
select 'test_SQL_04_ABC_99'
) as t
) select array_agg(ID) from t1
You seem to be using this inside a PL/pgSQL function. You should be using SELECT ... INTO variable FROM... instead:
declare
ids_list character varying[];
begin
.....
select array_agg(id)
into ids_list
from (
select house_guid
from tab
union
select guid
from tab
) t;
.... work with the ids_list variable
end;
The UNION will automatically remove all duplicates (as you tried to do with DISTINCT.
I'm looking to translate parsed structure to PostgreSQL. Hopefully, I am asking this correctly.
Is there code out there to do this already?
For more color, the need arose from this question/answer:
https://dba.stackexchange.com/questions/162784/postgresql-translating-user-defined-calculations-into-executable-calculation-in
Note this part of the question: "Use an off-the-shelf solution that can translate the parsed structure to SQL. Most languages have something that can do this, like SQL::Abstract. If not, you gotta create it."
Edit: We are using PostgreSQL 9.3.5, if it matters.
Probably query is too complicate, but it does what do you need :-)
Parametrs:
cmd - just your structure to parsing
op - possible operations
tables - jsonb object for translating table names from short form to full (probably you just mean than 'b' -> 'bbg' and 'p' -> 'pulls' instead 'bp' -> 'bbg_pulls'). I run this query on 9.6 and use jsonb. You can change it to just json for 9.3
WITH q AS (
WITH param AS (
SELECT '[bp2][-1]/[bp5]'::text AS cmd,
'+-/%*'::text AS op,
'{"bp": "bbg_pools"}'::jsonb AS tables
), precmd AS (
SELECT btrim(replace(translate(cmd, '[]', ',,'), ',,', ','), ',') AS precmd
FROM param
), split AS (
SELECT i,
split_part(precmd, ',', i) AS part
FROM (
SELECT generate_series(1, length(precmd) - length(translate(precmd, ',', '')) + 1) AS i,
precmd
FROM precmd
) AS a
) SELECT *,
CASE
WHEN part ~ ('^[' || op || ']$') THEN
' ) ' || part || ' ( '
WHEN tables->>(translate(part, '0123456789', '')) != '' THEN
'select val from '::text || (tables->>(translate(part, '0123456789', '0'))) || ' where id = ' || translate(part, translate(part, '0123456789', '0'), '')
WHEN part ~ '^[-]?[0-9]*$' THEN
' and val_date = (CURRENT_TIMESTAMP + (''' || part|| ' day'')::interval)::date '
ELSE
' ERROR '
END AS res
FROM param, precmd, split
ORDER BY i
)
SELECT 'SELECT (' || string_agg(res, ' ') || ')'
FROM q;
Some explanation (for better understanding you can try run query with SELECT * FROM q instead aggregating).
param CTE is just your paramters. In precmd I prepare cmd to split on parts and in split I do it.
Result of this query is:
SELECT (select val from bbg_pools where id = 2 and val_date = (CURRENT_TIMESTAMP + ('-1 day')::interval)::date ) / ( select val from bbg_pools where id = 5)
I need to write a query to compare column by column (ie: find differences) between two rows in the database. For example:
row1: 10 40 sometext 24
row2: 10 25 sometext 24
After the query executed, it should shows only the fields that have difference (ie: the second field)
Here's what I have done so far:
select table1.column1, table1.column2, table1.column3, table1.column4
from table1
where somefield in (field1, field2);
The above query will show me two rows one above another like this:
10 40 sometext 24
10 25 sometext 24
Then I have to manually do the comparison and it takes a lot of time b/c the row contains a lot of column.
So again my question is: How can I write a query that will show me only the columns that have differences??
Thanks
Use UNPIVOT clause (see http://www.oracle-developer.net/display.php?id=506) to turn columns into rows, then filter out the same rows (using GROUP BY HAVING COUNT and finally use PIVOT to get rows with different columns only.
To do this easily you need to query the metadata for the table to get each row. You can use the following code as a script.
Replace the define table_name with your table name and define yes_drop_it = NO. Put your raw WHERE syntax into the where_clause. The comparison logic always compares the first two rows returned for the where clause.
whenever sqlerror exit failure rollback;
set linesize 150
define test_tab_name = tst_cf_cols
define yes_drop_it = YES
define order_by = 1, 2
define where_clause = 1 = 1
define tab_owner = user
<<clearfirst>> begin
for clearout in (
select 'drop table ' || table_name as cmd
from all_tables
where owner = &&tab_owner and table_name = upper('&&test_tab_name')
and '&&yes_drop_it' = 'YES'
) loop
execute immediate clearout.cmd;
execute immediate '
create table &&test_tab_name as
select 10 as column1, 40 as column2, ''sometext'' as column3, 24 as column4 from dual
union all
select 10 as column1, 25 as column2, ''sometext'' as column3, 24 as column4 from dual
';
end loop;
end;
/
column cfsynt format a4000 word_wrap new_value comparison_syntax
with parms as (select 'parmquery' as cte_name, 'row_a' as corr_name_1, 'row_b' as corr_name_2 from dual)
select
'select * from (select ' || LISTAGG(cfcol || ' AS cf_' || trim (to_char (column_id, '000')) || '_' || column_name
, chr(13) || ', ') WITHIN GROUP (order by column_id)
|| chr(13) || ' from (select * from parmquery where row_number = 1) ' || corr_name_1
|| chr(13) || ', (select * from parmquery where row_number = 2) ' || corr_name_2
|| chr(13) || ') where ''DIFFERENT'' IN (' || LISTAGG ('cf_' || trim (to_char (column_id, '000')) || '_' || column_name, chr(13) || ', ') within group (order by column_id) || ')'
as cfsynt
from parms, (
select
'decode (' || corr_name_1 || '.' || column_name || ', ' || corr_name_2
|| '.' || column_name || ', ''SAME'', ''DIFFERENT'')'
as cfcol,
column_name,
column_id
from
parms,
all_tab_columns
where
owner = &&tab_owner and table_name = upper ('&&test_tab_name')
);
with parmquery as (select rownum as row_number, vals.* from (
select * from &&test_tab_name
where &&where_clause
order by &&order_by
) vals
) &&comparison_syntax
;
Assume that the DBA_TAB_COLUMNS looks like this:
I'd like to write a SQL or PL/SQL script to generate following text:
select 'NULL' as A1, B1, QUERY, RECORD_KEY from SMHIST.probsummarym1
union all
select 'NULL' as A1, 'NULL' as B1, QUERY, RECORD_KEY from SMHIST_EIT200.probsummarym1
union all
select A1, 'NULL' as B1, QUERY, RECORD_KEY from SMHIST_EIT300.probsummarym1
the requirements are:
If the table under any of the SMHIST% schemas do not have that column, then insert a default NULL alias for that columns.
the column list is in alphabetical order.
so can anybody tell me how to write this script?
EDIT: Added better alias names and en explicit CROSS JOIN. Added XMLAGG version.
NB: LISTAGG exists from Oracle version 11.2 and onwards and returns VARCHAR2. If the output string is larger than 4000K or if on a prior version you can use XMLAGG which is a bit more cumbersome to work with (eg. http://psoug.org/definition/xmlagg.htm).
With LISTAGG (returning VARCHAR2):
SELECT LISTAGG (line,
CHR (13) || CHR (10) || 'union all' || CHR (13) || CHR (10))
WITHIN GROUP (ORDER BY sortorder)
script
FROM (SELECT line, ROWNUM sortorder
FROM ( SELECT 'select '
|| LISTAGG (
CASE
WHEN tc.column_name IS NULL
THEN
'''NULL'' as '
END
|| col_join.column_name,
', ')
WITHIN GROUP (ORDER BY col_join.column_name)
|| ' from '
|| col_join.owner
|| '.'
|| col_join.table_name
line
FROM dba_tab_columns tc
RIGHT OUTER JOIN
(SELECT DISTINCT
owner, table_name, col_list.column_name
FROM dba_tab_columns
CROSS JOIN
(SELECT DISTINCT column_name
FROM dba_tab_columns
WHERE owner LIKE 'SMHIST%') col_list
WHERE owner LIKE 'SMHIST%') col_join
ON tc.owner = col_join.owner
AND tc.table_name = col_join.table_name
AND tc.column_name = col_join.column_name
GROUP BY col_join.owner, col_join.table_name
ORDER BY col_join.owner, col_join.table_name))
With XMLAGG (returning CLOB by adding .getclobval (), note: RTRIM works here because table names cannot include ',' and ' ' (space)):
SELECT REPLACE (SUBSTR (script, 1, LENGTH (script) - 12),
'&' || 'apos;',
'''')
FROM (SELECT XMLAGG (
XMLELEMENT (
e,
line,
CHR (13)
|| CHR (10)
|| 'union all'
|| CHR (13)
|| CHR (10))).EXTRACT ('//text()').getclobval ()
script
FROM (SELECT line, ROWNUM sortorder
FROM ( SELECT 'select '
|| RTRIM (
REPLACE (
XMLAGG (XMLELEMENT (
e,
CASE
WHEN tc.column_name
IS NULL
THEN
'''NULL'' as '
END
|| col_join.column_name,
', ') ORDER BY
col_join.column_name).EXTRACT (
'//text()').getclobval (),
'&' || 'apos;',
''''),
', ')
|| ' from '
|| col_join.owner
|| '.'
|| col_join.table_name
line
FROM dba_tab_columns tc
RIGHT OUTER JOIN
(SELECT DISTINCT
owner,
table_name,
col_list.column_name
FROM dba_tab_columns
CROSS JOIN
(SELECT DISTINCT column_name
FROM dba_tab_columns
WHERE owner LIKE 'SMHIST%') col_list
WHERE owner LIKE 'SMHIST%') col_join
ON tc.owner = col_join.owner
AND tc.table_name = col_join.table_name
AND tc.column_name = col_join.column_name
GROUP BY col_join.owner, col_join.table_name
ORDER BY col_join.owner, col_join.table_name)))