awk : trying to generate a query using awk - bash

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

Related

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

Oracle Ranking query

Need help to achieve below result:
source data:
Output Expected:
Query to generate source data:
SELECT '43443' AS MSISDN,'Turkey' AS LOC,TRUNC(SYSDATE) AS DATA_DAY FROM DUAL
UNION
SELECT '43443' AS MSISDN,'Turkey' AS LOC,TRUNC(SYSDATE-1) AS DATA_DAY FROM DUAL
UNION
SELECT '43443' AS MSISDN,'India' AS LOC,TRUNC(SYSDATE-2) AS DATA_DAY FROM DUAL
UNION
SELECT '43443' AS MSISDN,'Eng' AS LOC,TRUNC(SYSDATE-3) AS DATA_DAY FROM DUAL
UNION
SELECT '43446' AS MSISDN,'Eng' AS LOC,TRUNC(SYSDATE-4) AS DATA_DAY FROM DUAL
UNION
SELECT '43446' AS MSISDN,'India' AS LOC,TRUNC(SYSDATE-5) AS DATA_DAY FROM DUAL;

Determine start of data's most recent uninterrupted 'streak' by date

I have a dataset that looks something like:
asset_id,date_logged
1234,2018-02-01
1234,2018-02-02
1234,2018-02-03
1234,2018-02-04
1234,2018-02-05
1234,2018-02-06
1234,2018-02-07
1234,2018-02-08
1234,2018-02-09
1234,2018-02-10
9876,2018-02-01
9876,2018-02-02
9876,2018-02-03
9876,2018-02-07
9876,2018-02-08
9876,2018-02-09
9876,2018-02-10
For the purpose of this exercise, imagine today's date is 2018-02-10 (10 Feb 2018). For all the asset_ids in the table, I am trying to identify the start of the most recent unbroken streak for date_logged.
For asset_id = 1234, this would be 2018-02-01. The asset_id was logged all 10 days in an unbroken streak. For asset_id = 9876, this would be 2018-02-07. Because the asset_id was not logged on 2018-02-04, 2018-02-05, and 2018-02-06, the most recent unbroken streak starts on 2018-02-07.
So, my result set would hopefully look something like:
asset_id,Number_of_days_in_most_recent_logging_streak
1234,10
9876,4
Or, alternatively:
asset_id,Date_Begin_Most_Recent_Streak
1234,2018-02-01
9876,2018-02-07
I haven't been able to work out anything that gets me close -- my best effort so far is to get the number of days since the first log date and today, and the number of days the asset_id appears in the dataset, and compare these to identify situations where the streak is more recent than the first day they appear. For my real dataset this isn't particularly problematic, but it's an ugly solution and I would like to understand a better way of getting to the outcome.
Perhaps something like this. Break the query after each inline view in the WITH clause and SELECT * FROM the most recent inline view, to see what each step does.
with
inputs ( asset_id, date_logged ) as (
select 1234, to_date('2018-02-01', 'yyyy-mm-dd') from dual union all
select 1234, to_date('2018-02-02', 'yyyy-mm-dd') from dual union all
select 1234, to_date('2018-02-03', 'yyyy-mm-dd') from dual union all
select 1234, to_date('2018-02-04', 'yyyy-mm-dd') from dual union all
select 1234, to_date('2018-02-05', 'yyyy-mm-dd') from dual union all
select 1234, to_date('2018-02-06', 'yyyy-mm-dd') from dual union all
select 1234, to_date('2018-02-07', 'yyyy-mm-dd') from dual union all
select 1234, to_date('2018-02-08', 'yyyy-mm-dd') from dual union all
select 1234, to_date('2018-02-09', 'yyyy-mm-dd') from dual union all
select 1234, to_date('2018-02-10', 'yyyy-mm-dd') from dual union all
select 9876, to_date('2018-02-01', 'yyyy-mm-dd') from dual union all
select 9876, to_date('2018-02-02', 'yyyy-mm-dd') from dual union all
select 9876, to_date('2018-02-03', 'yyyy-mm-dd') from dual union all
select 9876, to_date('2018-02-07', 'yyyy-mm-dd') from dual union all
select 9876, to_date('2018-02-08', 'yyyy-mm-dd') from dual union all
select 9876, to_date('2018-02-09', 'yyyy-mm-dd') from dual union all
select 9876, to_date('2018-02-10', 'yyyy-mm-dd') from dual
),
prep ( asset_id, date_logged, grp ) as (
select asset_id, date_logged,
date_logged - row_number()
over (partition by asset_id order by date_logged)
from inputs
),
agg ( asset_id, date_logged, cnt ) as (
select asset_id, min(date_logged), count(*)
from prep
group by asset_id, grp
)
select asset_id, max(date_logged) as date_start_recent_streak,
max(cnt) keep (dense_rank last order by date_logged) as cnt
from agg
group by asset_id
order by asset_id -- If needed
;
ASSET_ID DATE_START_RECENT_STREAK CNT
---------- ------------------------ ----------
1234 2018-02-01 10
9876 2018-02-07 4
you can try this,
with test (asset_id, date_logged) as
(select 1234, date '2018-02-01' from dual union all
select 1234, date '2018-02-02' from dual union all
select 1234, date '2018-02-03' from dual union all
select 1234, date '2018-02-04' from dual union all
select 1234, date '2018-02-05' from dual union all
select 1234, date '2018-02-06' from dual union all
select 1234, date '2018-02-07' from dual union all
select 1234, date '2018-02-08' from dual union all
select 1234, date '2018-02-09' from dual union all
select 1234, date '2018-02-10' from dual union all
select 9876, date '2018-02-01' from dual union all
select 9876, date '2018-02-02' from dual union all
select 9876, date '2018-02-03' from dual union all
select 9876, date '2018-02-07' from dual union all
select 9876, date '2018-02-08' from dual union all
select 9876, date '2018-02-09' from dual union all
select 9876, date '2018-02-10' from dual union all
select 9876, date '2018-02-11' from dual union all
select 9876, date '2018-02-12' from dual
)
SELECT asset_id, MIN(date_logged), COUNT(1)
FROM (SELECT asset_id, date_logged,
MAX(date_logged) OVER (PARTITION BY asset_id)+1 max_date_logged_plus_one,
DENSE_RANK() OVER (PARTITION BY asset_id ORDER BY date_logged desc) rown
FROM test
ORDER BY asset_id, date_logged desc)
WHERE max_date_logged_plus_one - date_logged = rown
GROUP BY asset_id;
ASSET_ID MIN(DATE_LOGGED) COUNT(1)
---------- ---------------- ----------
1234 01-FEB-18 10
9876 07-FEB-18 6
if below data is commented, output is
select 9876, date '2018-02-10' from dual union all
ASSET_ID MIN(DATE_LOGGED) COUNT(1)
---------- ---------------- ----------
1234 01-FEB-18 10
9876 11-FEB-18 2
Would this make any sense?
SQL> with test (asset_id, date_logged) as
2 (select 1234, date '2018-02-01' from dual union all
3 select 1234, date '2018-02-02' from dual union all
4 select 1234, date '2018-02-03' from dual union all
5 select 1234, date '2018-02-04' from dual union all
6 select 1234, date '2018-02-05' from dual union all
7 select 1234, date '2018-02-06' from dual union all
8 select 1234, date '2018-02-07' from dual union all
9 select 1234, date '2018-02-08' from dual union all
10 select 1234, date '2018-02-09' from dual union all
11 select 1234, date '2018-02-10' from dual union all
12 select 9876, date '2018-02-01' from dual union all
13 select 9876, date '2018-02-02' from dual union all
14 select 9876, date '2018-02-03' from dual union all
15 select 9876, date '2018-02-07' from dual union all
16 select 9876, date '2018-02-08' from dual union all
17 select 9876, date '2018-02-09' from dual union all
18 select 9876, date '2018-02-10' from dual
19 ),
20 inter as
21 -- difference between DATE_LOGGED and its previous DATE_LOGGED
22 (select asset_id,
23 date_logged,
24 date_logged - lag(date_logged) over (partition by asset_id order by date_logged) diff
25 from test
26 )
27 select i.asset_id, min(i.date_logged) date_logged
28 from inter i
29 where nvl(i.diff, 1) = (select max(i1.diff) from inter i1
30 where i1.asset_id = i.asset_id
31 )
32 group by i.asset_id
33 order by i.asset_id;
ASSET_ID DATE_LOGGE
---------- ----------
1234 2018-02-01
9876 2018-02-07
SQL>

oracle select aplhanumeric value from the column

In oracle I have a table teststring and column name is STRINGVALUE and values in the column are :
A1CC
A2BB
C1DD
C2CC
ABA28
1B333
AB345
1A222
2NDDD
I have to select only those value which has first 2 values alphanumeric like A1CC,A2BB,1B33,2NDDD etc
SELECT stringvalue FROM teststring
WHERE regexp_like(substr(stringvalue,1,2),'[A-Z][0-9]|[0-9][A-Z]');
or
SELECT stringvalue FROM teststring
WHERE regexp_like(substr(stringvalue,1,2),'[[:alpha:]][[:digit:]]|[[:digit:]][[:alpha:]]');
select only those value which has first 2 values alphanumeric
Your example output doesn't satisfy the rule you mentioned. According to your rule, this is the test case :
SQL> WITH DATA AS(
2 SELECT 'A1CC' STR FROM DUAL UNION ALL
3 SELECT 'A2BB' STR FROM DUAL UNION ALL
4 SELECT 'C1DD' str from dual union all
5 SELECT 'C2CC' STR FROM DUAL UNION ALL
6 SELECT 'ABA28' str from dual union all
7 SELECT '1B333' STR FROM DUAL UNION ALL
8 SELECT 'AB345' STR FROM DUAL UNION ALL
9 SELECT '1A222' STR FROM DUAL UNION ALL
10 SELECT '2NDDD' FROM DUAL)
11 SELECT STR
12 FROM DATA
13 WHERE (REGEXP_LIKE(SUBSTR(STR,1,1),'[[:alpha:]]')
14 AND REGEXP_LIKE(SUBSTR(STR,2,1),'[[:digit:]]'))
15 OR (REGEXP_LIKE(SUBSTR(STR,1,1),'[[:digit:]]')
16 AND REGEXP_LIKE(SUBSTR(STR,2,1),'[[:alpha:]]'))
17 /
STR
-----
A1CC
A2BB
C1DD
C2CC
1B333
1A222
2NDDD
7 rows selected.
SQL>
Thanks Lalit, when i was using regular expression i was missing the keyword digit thats why i was unable to get the right result set.
in my requirement i have more than thousand value in that column so simply i have called the calum name from the table instead of using the dual union and its working fine..
the new query where any number of record available in the column below format would be feasible.
WITH DATA AS(SELECT STRINGVALUE as STR FROM TESTSTRING )
SELECT STR
FROM DATA
WHERE (REGEXP_LIKE(SUBSTR(STR,1,1),'[[:alpha:]]')
AND REGEXP_LIKE(SUBSTR(STR,2,1),'[[:digit:]]'))
OR (REGEXP_LIKE(SUBSTR(STR,1,1),'[[:digit:]]')
AND REGEXP_LIKE(SUBSTR(STR,2,1),'[[:alpha:]]'))
Use this Query
with TAB(TXT) as(
select 'A1CC' from dual union all
select 'A2BB' from dual union all
select 'C1DD' from dual union all
select 'C2CC' from dual union all
select 'ABA28' from dual union all
select '1B333' from dual union all
select 'AB345' from dual union all
select '1A222' from dual union all
select '2NDDD' from dual)
----------------
--- End of Data
----------------
select txt
from tab
where (regexp_like(TXT, '^((\w\d)|(\d\w)){1}\w+'));
To get output like:
| TXT |
|-------|
| A1CC |
| A2BB |
| C1DD |
| C2CC |
| 1B333 |
| 1A222 |
| 2NDDD |

Duplicate rows with spaces:

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

Resources