Duplicate rows with spaces: - oracle

I have a dataset as below:
SELECT ' 1234 ' ID,NULL TAG,' AB' CODE FROM DUAL
UNION ALL
SELECT '453' ID,'GEN' TAG,'AB' CODE FROM DUAL
UNION ALL
SELECT '1234' ID,NULL TAG,' AB' CODE FROM DUAL)
I am trying to get duplicates with the below query. The output should be row 1 and 3 but i get only one row as trimmed output of 1 or 3.
SELECT TRIM(ID),TRIM(TAG),TRIM(CODE) FROM
(SELECT ' 1234 ' ID,NULL TAG,' AB' CODE FROM DUAL
UNION ALL
SELECT '453' ID,'GEN' TAG,'AB' CODE FROM DUAL
UNION ALL
SELECT '1234' ID,NULL TAG,' AB' CODE FROM DUAL)
WHERE (TRIM(ID),TRIM(TAG),TRIM(CODE)) IN
(
SELECT TRIM(ID),TRIM(TAG),TRIM(CODE) FROM
(SELECT ' 1234 ' ID,NULL TAG,' AB' CODE FROM DUAL
UNION ALL
SELECT '453' ID,'GEN' TAG,'AB' CODE FROM DUAL
UNION ALL
SELECT '1234' ID,NULL TAG,' AB' CODE FROM DUAL)
GROUP BY TRIM(ID),TRIM(TAG),TRIM(CODE)
HAVING COUNT(*) >1
)

I just ran this and it returned rows 1 and 3:
SELECT TRIM(ID),TRIM(TAG),TRIM(CODE)
FROM
(
SELECT ' 1234 ' ID,NULL TAG,' AB' CODE FROM DUAL
UNION ALL
SELECT '453' ID,'GEN' TAG,'AB' CODE FROM DUAL
UNION ALL
SELECT '1234' ID,NULL TAG,' AB' CODE FROM DUAL
)
WHERE TRIM(ID) IN
(
SELECT TRIM(ID)
FROM
(
SELECT ' 1234 ' ID,NULL TAG,' AB' CODE FROM DUAL
UNION ALL
SELECT '453' ID,'GEN' TAG,'AB' CODE FROM DUAL
UNION ALL
SELECT '1234' ID,NULL TAG,' AB' CODE FROM DUAL
)
GROUP BY TRIM(ID),TRIM(TAG),TRIM(CODE)
HAVING COUNT(*) >1
)
I changed your WHERE to reference only the TRIM(ID) instead of all 3 values.
Edit #1, part of the problem is you are comparing null to null which you cannot do. So you can do a null check on the columns and if it is null then replace it. I wrapped the null columns with nvl(null, 'na') so then it had a value to compare:
SELECT TRIM(ID) id,TRIM(TAG) tag,TRIM(CODE) code
FROM
(
SELECT ' 1234 ' ID, nvl(null, 'na') TAG,' AB' CODE FROM DUAL
UNION ALL
SELECT '453' ID,'GEN' TAG,'AB' CODE FROM DUAL
UNION ALL
SELECT ' 1234 ' ID,nvl(null, 'na') TAG,' AB' CODE FROM DUAL
UNION ALL
SELECT ' 1234 ' ID,nvl(null, 'na') TAG,' AC' CODE FROM DUAL
)
WHERE (TRIM(ID),TRIM(TAG),TRIM(CODE)) IN
(
SELECT TRIM(ID),TRIM(TAG),TRIM(CODE)
FROM
(
SELECT ' 1234 ' ID,nvl(null, 'na') TAG,' AB' CODE FROM DUAL
UNION ALL
SELECT '453' ID,'GEN' TAG,'AB' CODE FROM DUAL
UNION ALL
SELECT ' 1234 ' ID,nvl(null, 'na') TAG,' AB' CODE FROM DUAL
UNION ALL
SELECT ' 1234 ' ID,nvl(null, 'na') TAG,' AC' CODE FROM DUAL
)
GROUP BY TRIM(ID), TRIM(CODE), TRIM(TAG)
HAVING COUNT(*) >1
)
See SQL Fiddle with Demo

Related

Group By inside Rtrim(Xmlagg (Xmlelement (e,element || ',')).extract ( '//text()' ).GetClobVal(), ',')

I need to group values ​​inside a query using (or not) the command Rtrim(Xmlagg (Xmlelement (e,column || ',')).extract ( '//text()' ).GetClobVal(), ','), but I can't find any literature where explain a way to group the data inside this command. The code is very simple, as you can see below:
SELECT ID,
Rtrim(Xmlagg (Xmlelement (and, CONTRACTS || ',')).extract ( '//text()' ).GetClobVal(), ',') AS CONTRACTS
FROM TABLE_A
GROUP BY ID
The result in CONTRACTS is always repeated when the ID is found, thats ok, it´s working!
ID
CONTRACTS
876
1,1,1,2,3,3
But what I really need is this return:
ID
CONTRACTS
876
1,2,3
It´s not necessary to use the command Rtrim(Xmlagg (Xmlelement (e,column || ',')).extract ( '//text()' ).GetClobVal(), ','), instead, I just use to concatenate element with comma "," in the same column.
If anyone can help me, I would be very grateful!
If your values will fit into a VARCHAR2 data type (rather than a CLOB) then you can use a nested sub-query to get the DISTINCT values for each ID:
SELECT ID,
LISTAGG(contracts, ',') WITHIN GROUP (ORDER BY contracts) AS CONTRACTS
FROM ( SELECT DISTINCT id, contracts FROM TABLE_A)
GROUP BY ID
Or, from Oracle 19c, it is built-in to LISTAGG:
SELECT ID,
LISTAGG(DISTINCT contracts, ',') WITHIN GROUP (ORDER BY contracts) AS CONTRACTS
FROM TABLE_A
GROUP BY ID
If you want a CLOB then you can use the same technique as the first query:
SELECT ID,
Rtrim(
Xmlagg(
Xmlelement(name, CONTRACTS || ',')
ORDER BY contracts
).extract ( '//text()' ).GetClobVal(),
','
) AS CONTRACTS
FROM (SELECT DISTINCT id, contracts FROM TABLE_A)
GROUP BY ID
Which, for the sample data:
CREATE TABLE table_a (id, contracts) AS
SELECT 876, 1 FROM DUAL UNION ALL
SELECT 876, 1 FROM DUAL UNION ALL
SELECT 876, 1 FROM DUAL UNION ALL
SELECT 876, 2 FROM DUAL UNION ALL
SELECT 876, 2 FROM DUAL UNION ALL
SELECT 876, 3 FROM DUAL UNION ALL
SELECT 876, 3 FROM DUAL UNION ALL
SELECT 876, 3 FROM DUAL;
All output:
ID
CONTRACTS
876
1,2,3
db<>fiddle here
It's much easier to do all those operation in XML functions: DBFiddle
SELECT--+ NO_XML_QUERY_REWRITE
id,
xmlquery(
'string-join(distinct-values($R/R/X/text()),",")'
passing
Xmlelement(
R,
Xmlagg(
Xmlelement (X, CONTRACTS)
order by CONTRACTS
)) as R
RETURNING CONTENT
) AS CONTRACTS
FROM TABLE_A
GROUP BY ID;
Full example with test data:
with table_a (id, contracts) AS (
SELECT 876, 1 FROM DUAL UNION ALL
SELECT 876, 1 FROM DUAL UNION ALL
SELECT 876, 1 FROM DUAL UNION ALL
SELECT 876, 2 FROM DUAL UNION ALL
SELECT 876, 2 FROM DUAL UNION ALL
SELECT 876, 3 FROM DUAL UNION ALL
SELECT 876, 3 FROM DUAL UNION ALL
SELECT 876, 3 FROM DUAL
)
SELECT--+ NO_XML_QUERY_REWRITE
id,
xmlquery(
'string-join(distinct-values($R/R/X/text()),",")'
passing
Xmlelement(
R,
Xmlagg(
Xmlelement (X, CONTRACTS)
order by CONTRACTS
)) as R
RETURNING CONTENT
) AS CONTRACTS
FROM TABLE_A
GROUP BY ID;

Duplicated rows numbering

I need to number the rows so that the row number with the same ID is the same. For example:
Oracle database. Any ideas?
Use the DENSE_RANK analytic function:
SELECT DENSE_RANK() OVER (ORDER BY id) AS row_number,
id
FROM your_table
Which, for the sample data:
CREATE TABLE your_table ( id ) AS
SELECT 86325 FROM DUAL UNION ALL
SELECT 86325 FROM DUAL UNION ALL
SELECT 86326 FROM DUAL UNION ALL
SELECT 86326 FROM DUAL UNION ALL
SELECT 86352 FROM DUAL UNION ALL
SELECT 86353 FROM DUAL UNION ALL
SELECT 86354 FROM DUAL UNION ALL
SELECT 86354 FROM DUAL;
Outputs:
ROW_NUMBER
ID
1
86325
1
86325
2
86326
2
86326
3
86352
4
86353
5
86354
5
86354
db<>fiddle here

REGEXP_SUBSTR not able to process only current row

(SELECT LISTAGG(EVENT_DESC, ',') WITHIN GROUP (ORDER BY EVENT_DESC) FROM EVENT_REF WHERE EVENT_ID IN
( SELECT REGEXP_SUBSTR(AFTER_VALUE,'[^,]+', 1, level) FROM DUAL
CONNECT BY REGEXP_SUBSTR(AFTER_VALUE, '[^,]+', 1, level) IS NOT NULL
)
)
A table from which I am fetching AFTER_VALUE has values of integer which is comma seperated like
AFTER_VALUE data
Expected output
1
Event1
1,2
Event1,Event2
1,12,2,5
Event1,Event12,Event2,Event5
15,13
Event15,Event13
these are Ids in EVENT_REF table which have some description. I am trying to basically present
ex. 1,2 as Event1, Event2 and send back from query. There are multiple events so using REPLACE would be very tedious.
When using above query I'm getting error as “ORA-01722: invalid number” whenever there is more than one value in AFTER_VALUE column Ex. if there exists only one id , then the query works but for values like 1,2 or 1,13 etc it throws invalid number error.
PS: The event names are not Event1,Event2 etc , I have just put for reference.
You don't even need regular expressions for this assignment. Standard string function replace() can do the same thing, and faster. You only need an extra 'Event' at the beginning of the string, since that one doesn't "replace" anything.
Like this: (note that you don't need the with clause; I included it only for quick testing)
with
event_ref (after_value) as (
select '1' from dual union all
select '1,2' from dual union all
select '1,12,2,5' from dual union all
select '15,13' from dual
)
select after_value,
'Event' || replace(after_value, ',', ',Event') as desired_output
from event_ref
;
AFTER_VALUE DESIRED_OUTPUT
----------- -----------------------------
1 Event1
1,2 Event1,Event2
1,12,2,5 Event1,Event12,Event2,Event5
15,13 Event15,Event13
Ah,ok, looks, like you have other characters in your comma-separated list, so you can use this query:
with EVENT_REF(EVENT_ID,EVENT_DESC) as (
select 1, 'Desc 1' from dual union all
select 2, 'Desc 2' from dual union all
select 3, 'Desc 3' from dual union all
select 4, 'Desc 4' from dual union all
select 5, 'Desc 5' from dual union all
select 12, 'Desc12' from dual union all
select 13, 'Desc13' from dual union all
select 15, 'Desc15' from dual
)
select
(SELECT LISTAGG(EVENT_DESC, ',')
WITHIN GROUP (ORDER BY EVENT_DESC)
FROM EVENT_REF
WHERE EVENT_ID IN
( SELECT to_number(REGEXP_SUBSTR(AFTER_VALUE,'\d+', 1, level))
FROM DUAL
CONNECT BY level<=REGEXP_COUNT(AFTER_VALUE, '\d+')
)
)
from (
select '1' AFTER_VALUE from dual union all
select '1,2' AFTER_VALUE from dual union all
select '1,12,2,5' AFTER_VALUE from dual union all
select '15,13' AFTER_VALUE from dual
);
PS. And do not forget that to_number has 'default on conversion error' now: https://docs.oracle.com/en/database/oracle/oracle-database/12.2/sqlrf/TO_NUMBER.html
There is no need to split and concatenate substrings, just use regexp_replace:
with EVENT_REF (AFTER_VALUE) as (
select '1' from dual union all
select '1,2' from dual union all
select '1,12,2,5' from dual union all
select '15,13' from dual
)
select regexp_replace(AFTER_VALUE,'(\d+)','Event\1') from EVENT_REF;
REGEXP_REPLACE(AFTER_VALUE,'(\D+)','EVENT\1')
-----------------------------------------------
Event1
Event1,Event2
Event1,Event12,Event2,Event5
Event15,Event13

awk : trying to generate a query using awk

I'm trying to write an awk program to generate a sql query, using the output from a pipe command. The output of the command will be something like this
Service name: APP1
Service name: APP2
Service name: APP3
Service name: APP4
Service name: APP5
Service name: APP6
Service name: APP7
Service name: APP8
Service name: APP9
and the result I need is something like :
select 'APP1' x from dual union all
select 'APP2' from dual union all
select 'APP3' from dual union all
select 'APP4' from dual union all
select 'APP5' from dual union all
select 'APP6' from dual union all
select 'APP7' from dual union all
select 'APP8' from dual union all
select 'APP9' from dual
I need to get the string after the "Service Name:" string, put it between quotes, and place it into the select.
The first line must have the "x" after the string , and the last line must not contain the union all. There cannot be a space on the string. Since I don't have much experience with awk, so far I couldn't figure a way to do it.
I have this so far:
srvctl config service -db database | grep 'Service name' | awk 'BEGIN {FS = "[:]"}
{ gsub(/^[ \t]+|[ \t]+$/, "", $2)
if ( NR == 1 )
{
printf "'select\ \''" $2 "'\'\ x\ from\ dual\ union\ all\ '\n"
}
else
{
printf "'select\ \''" $2 "'\'\ from\ dual\ union\ all\ '\n"
}
}'
It will generate the following output:
select 'APP1' x from dual union all
select 'APP2' from dual union all
select 'APP3' from dual union all
select 'APP4' from dual union all
select 'APP5' from dual union all
select 'APP6' from dual union all
select 'APP7' from dual union all
select 'APP8' from dual union all
select 'APP9' from dual union all
Any help is appreciated
Thanks
Using awk you can do:
awk 'NR==1{printf "select \047%s\047 x from dual union all\n", $NF; next}
s{print s, "union all"}
{s=sprintf("select \047%s\047 from dual", $NF)} END{print s}' file
select 'APP1' x from dual union all
select 'APP2' from dual union all
select 'APP3' from dual union all
select 'APP4' from dual union all
select 'APP5' from dual union all
select 'APP6' from dual union all
select 'APP7' from dual union all
select 'APP8' from dual union all
select 'APP9' from dual
perhaps simpler this way
$ awk -v q="'" '{print "select " q$3q (NR==1?" x":"") " from dual union all"}' file |
sed '$s/\w* \w*$//'
select 'APP1' x from dual union all
select 'APP2' from dual union all
select 'APP3' from dual union all
select 'APP4' from dual union all
select 'APP5' from dual union all
select 'APP6' from dual union all
select 'APP7' from dual union all
select 'APP8' from dual union all
select 'APP9' from dual
or
$ awk -v q="'" -v x=" x" '{print "select " q$3q x " from dual union all"; x=""}' file |
sed '$s/\w* \w*$//'
$ awk 'BEGIN{x=" x"} NR>1{print prev " union all"; x=""} {prev="select \047" $NF "\047" x " from dual"} END{print prev}' file
select 'APP1' x from dual union all
select 'APP2' from dual union all
select 'APP3' from dual union all
select 'APP4' from dual union all
select 'APP5' from dual union all
select 'APP6' from dual union all
select 'APP7' from dual union all
select 'APP8' from dual union all
select 'APP9' from dual

Oracle - Select all values from in clause even if there is no match

I have a dynamic data set such as 'AAA','TTT','CCC','FFF'
I need to match this data against a column C in a table T
e.g. I have in the table T for Column C, 'AAA','BBB','DDD','FFF'
I need to return something like (show null if the value doesn't exist in Column)
'AAA'
'TTT' NULL
'CCC' NULL
'FFF'
I don't want to drop the set into table as my data changes frequently and need to query quickly.
Any ideas greatly appreciated.
Is this what you're after ??
with w_data as (
select 'AAA' c from dual union all
select 'TTT' c from dual union all
select 'CCC' c from dual union all
select 'FFF' c from dual
),
w_table_t as (
select 'AAA' c from dual union all
select 'BBB' c from dual union all
select 'DDD' c from dual union all
select 'FFF' c from dual
)
select d.c,
NVL2(t.c, '', 'NULL' )
from w_data d
LEFT OUTER JOIN
w_table_t t
ON t.c = d.c
/
results:
C NVL2
--- ----
AAA
FFF
TTT NULL
CCC NULL
Try this (EDITED) :
with data_set as (
select 'AAA' col from dual union
select 'TTT' col from dual union
select 'CCC' col from dual union
select 'FFF' col from dual
)
select case when d.col in (select column_C from table_T) then d.col
else d.col||' Null' end as col_name
from data_set d
/

Resources