I have a following table structure:
server_id
server_databases
1
[{"name": "mssql", "count": 12},{"name": "postgresql", "count": 1}]
2
[]
3
null
What I want to receive as a result(I want to keep servers 2 and 3 if it would be null or empty object doesn't matter):
server_id
databases
1
{"mssql": 12, "postgresql": 1}
2
null
3
null
I've tried to build json myself
SELECT server_id,
(
select '{' || listagg('"' || x.name || '":' || x.count, ',') || '}' as clientdatabases
from (
select cb."name"::varchar as name, sum(cb."count")::int as count from e.server_databases as cb group by name
) x
)
FROM my_table e
But it fails with interestiong error
[XX000] ERROR: Query unsupported due to an internal error. Detail: Unsupported witness case Where: nested_decorrelate_calc_witness_unsupported|calc_witness
It looks like PartiQL supports such cases, but I have no idea how to implement it. I will use UDF for now. But, if you can help me with a "native" solution, it would be amazing.
Update SQL script for case reproduction:
CREATE table my_table(server_id int, server_databases super);
insert into my_table(server_id, server_databases) values (
1, json_parse('[{"name": "mssql", "count": 12},{"name": "postgresql", "count": 1}]')
),
(2, json_parse('[]')),
(3, null);
SELECT server_id,
(
select '{' || listagg('"' || x.name || '":' || x.count, ',') || '}' as clientdatabases
from (
select cb."name"::varchar as name, sum(cb."count")::int as count from e.server_databases as cb group by name
) x
)
FROM my_table e;
Use ISNULL in count :
SELECT server_id,
(
select '{' || listagg('"' || x.name || '":' || x.count, ',') || '}' as clientdatabases
from (
select cb."name"::varchar as name, ISNULL(sum(cb."count")::int,0) as count from e.server_databases as cb group by name
) x
)
FROM my_table e
You can use left join for handling null cases
with b as (select server_id,
('{' || listagg('"' || x.name::text || '":' || x.count::int, ',') || '}') as clientdatabases
from my_table as e, e.server_databases as x
group by server_id)
select a.server_id, a.server_databases, json_parse(b.clientdatabases)
from my_table as a
left join b
on a.server_id = b.server_id;
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 have a "personnel basic information table", and it will be referenced by many "personnel subset tables" (there is no relationship between these subset tables, they are just a subset of the basic information of the personnel).
Now there is a requirement to merge the "personnel subset" into a column based on the person's foreign key and display it as a field of the "personnel basic information" (after the "personnel subset" is merged, each person record will only display one row) .
And the basic personnel information and personnel subset information will be passed into the query conditions, so these conditions are dynamically assembled.
It has been implemented now, but the problem is that the query needs to be optimized. Execution will be particularly slow once.
Database is oracle 11g.
Here is the sql generated by a certain query:
WITH in_hr_work_party_current AS (
SELECT
*
FROM
hr_work_party
WHERE
status = '1'
AND removaltime IS NULL
AND (
appointorg IS NOT NULL
OR NAME IS NOT NULL
)
),
qv_hr_work_party_current AS (
SELECT
empid,
listagg (
DECODE (
appointorg, NULL, '', 'oidstart' || appointorg || 'oidend-'
) || NVL (NAME, 'xx'),
','
) WITHIN GROUP (
ORDER BY
positiongrade ASC,
positionorder ASC,
appointtime ASC
) AS party_current_info
FROM
in_hr_work_party_current
GROUP BY
empid
),
in_position_postwork_main AS (
SELECT
po.empid,
working_coid_name AS post_main_coid_name,
working_poid_name AS post_main_poid_name,
officedepname AS post_main_officedepname,
post AS post_main_post,
posttype AS post_main_posttype,
workprof AS post_main_workprof,
starttime AS post_main_starttime,
FLOOR (
CEIL (
MONTHS_BETWEEN (
NVL (
TO_DATE (po.endtime, 'yyyy-MM-dd'),
SYSDATE
),
TO_DATE (po.starttime, 'yyyy-MM-dd')
)
) / 12
) || 'xx' AS post_main_years,
epo.entrytime AS post_main_entrytime,
ROW_NUMBER () OVER (
PARTITION BY po.empid
ORDER BY
po.opttime DESC
) AS rn
FROM
hr_work_postworkinfo po,
hr_work_empposition epo,
v_waf_ac_organ_base dep
WHERE
DEP.OID = PO.officedepid
AND epo.wepid = po.wepid
AND po.status = '1'
AND po.officetype = '00'
AND is_date (po.starttime) = 1
AND (
is_date (po.endtime) = 1
OR po.endtime IS NULL
)
AND epo.empsort = '01'
AND epo.subrelation = '01'
AND dep.orule LIKE '-xxxxx-%'
),
qv_position_postwork_main AS (
SELECT
*
FROM
in_position_postwork_main
WHERE
rn = 1
),
in_postworkinfo AS (
SELECT
po.*,
epo.entrytime,
epo.leavetime,
epo.empsort,
epo.empstatus,
epo.subrelation,
(
working_coid_name || DECODE (
working_poid_name, NULL, '', '-' || working_poid_name
) || DECODE (
officedepname, NULL, '', '-' || officedepname
)
) orgname,
FLOOR (
CEIL (
MONTHS_BETWEEN (
NVL (
TO_DATE (po.endtime, 'yyyy-MM-dd'),
SYSDATE
),
TO_DATE (po.starttime, 'yyyy-MM-dd')
)
) / 12
) postyear,
MOD (
CEIL (
MONTHS_BETWEEN (
NVL (
TO_DATE (po.endtime, 'yyyy-MM-dd'),
SYSDATE
),
TO_DATE (po.starttime, 'yyyy-MM-dd')
)
),
12
) postmonth,
ROW_NUMBER () OVER (
PARTITION BY po.empid
ORDER BY
po.starttime DESC
) rn,
COUNT (*) OVER (PARTITION BY po.empid) ct
FROM
hr_work_postworkinfo po,
HR_WORK_EMPPOSITION EPO,
V_WAF_AC_ORGAN_BASE DEP
WHERE
po.wepid = EPO.wepid
AND PO.officedepid = DEP.OID
AND is_date (starttime) = 1
AND (
is_date (endtime) = 1
OR endtime IS NULL
)
AND epo.empstatus != '05'
AND DEP.orule LIKE '-xxxxxx-%'
),
qv_hr_position_postwork AS (
SELECT
empid,
listagg (
'[' || starttime || 'xx' || NVL (endtime, 'xx') || 'xx' || orgname || 'xx' || NVL (post, 'xx') || 'xx' || NVL (posttype, 'xx') || 'xx' || DECODE (
postyear, 0, '', postyear || 'xx'
) || DECODE (
postmonth, 0, '', postmonth || 'xxxx'
) || 'xx' || NVL (postgradation, 'xx') || 'xxxx' || entrytime || 'xx' || empsort || 'xx' || empstatus || 'xx' || subrelation || ']',
','
) WITHIN GROUP (
ORDER BY
starttime DESC
) || (
CASE WHEN ct >= 5 THEN ',...' ELSE '' END
) work_info
FROM
in_postworkinfo
WHERE
rn <= 5
GROUP BY
empid,
ct
),
in_hr_emp_basicinfo AS (
SELECT
emp.*
FROM
hr_emp_basicinfo emp
),
qv_hr_emp_basicinfo_view AS (
SELECT
in_hr_emp_basicinfo.*
FROM
in_hr_emp_basicinfo
),
in_party AS (
SELECT
hr_work_party.NAME,
hr_work_party.empid,
hr_work_party.appointno,
hr_work_party.appointtime,
hr_work_party.removaltime,
hr_work_party.positiongrade,
NVL (
waf_ac_organ.NAME, hr_work_party.appointorg
) apporg,
ROW_NUMBER () OVER (
PARTITION BY hr_work_party.empid
ORDER BY
appointtime DESC,
removaltime DESC,
positiongrade ASC,
positionorder ASC
) rn,
COUNT (*) OVER (PARTITION BY hr_work_party.empid) ct
FROM
hr_work_party,
waf_ac_organ
WHERE
hr_work_party.status = '1'
AND hr_work_party.appointorg = waf_ac_organ.OID (+)
AND (
hr_work_party.appointorg IS NOT NULL
OR hr_work_party.NAME IS NOT NULL
)
),
qv_hr_work_party AS (
SELECT
empid,
listagg (
'[' || appointtime || 'xx' || NVL (removaltime, 'xx') || ' ' || apporg || '-' || NVL (NAME, 'xx') || DECODE (
NVL (appointno, 'xx'),
'xx',
'',
' - ' || appointno
) || ']',
','
) WITHIN GROUP (
ORDER BY
rn ASC
) || (
CASE WHEN ct > 10 THEN ',...' ELSE '' END
) app_party,
listagg (
'[' || appointtime || 'xx' || NVL (removaltime, 'xx') || ' ' || apporg || '-' || NVL (NAME, 'xx') || DECODE (
positiongrade, NULL, '', '(' || positiongrade || ')'
) || ']',
','
) WITHIN GROUP (
ORDER BY
rn ASC
) || (
CASE WHEN ct > 10 THEN ',...' ELSE '' END
) AS party_grade
FROM
in_party
WHERE
rn <= 10
GROUP BY
empid,
ct
)
SELECT
*
FROM
(
SELECT
dumb_table.*,
ROWNUM dumb_rn
FROM
(
SELECT
qv_hr_work_party_current.party_current_info,
qv_position_postwork_main.post_main_coid_name,
qv_position_postwork_main.post_main_poid_name,
qv_position_postwork_main.post_main_officedepname,
qv_position_postwork_main.post_main_years,
qv_hr_emp_basicinfo_view.age,
qv_hr_emp_basicinfo_view.NAME,
qv_hr_position_postwork.work_info
FROM
qv_hr_work_party_current,
qv_position_postwork_main,
qv_hr_work_party,
qv_hr_position_postwork,
qv_hr_emp_basicinfo_view
WHERE
1 = 1
AND qv_hr_emp_basicinfo_view.empid = qv_hr_work_party_current.empid(+)
AND qv_hr_emp_basicinfo_view.empid = qv_position_postwork_main.empid(+)
AND qv_hr_emp_basicinfo_view.empid = qv_hr_work_party.empid (+)
AND qv_hr_emp_basicinfo_view.empid = qv_hr_position_postwork.empid
AND qv_hr_emp_basicinfo_view.empid IS NOT NULL
ORDER BY
qv_hr_emp_basicinfo_view.sno
) dumb_table
)
WHERE
dumb_rn <= 30
AND dumb_rn > 0
enter image description here
There is a line '/home/pc/test',
and words in the tags '[/ alseko] [/ logs] [/ archive]'
How to get the expected result =
[/home/pc/test/alseko][/home/pc/test/logs][/home/pc/test/archive]
My code, I'm trying to find:
select '[' || '/home/pc/test' ||
ltrim(substr('[/alseko][/logs][/archive]',
instr('[/alseko][/logs][/archive]', '['),
instr('[/alseko][/logs][/archive]', ']')),'[')
from dual
You could split using REGEXP_SUBSTR and combine the paths using LISTAGG.
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE t
("LINE" varchar2(13), "TAGS" varchar2(31))
;
INSERT ALL
INTO t ("LINE", "TAGS")
VALUES ('/home/pc/test', '[/ alseko] [/ logs] [/ archive]')
SELECT * FROM dual
;
Query 1:
SELECT '[' || LISTAGG(paths, '][') WITHIN
GROUP (
ORDER BY NULL
) || ']' as all_paths
FROM (
SELECT line || '/' || REGEXP_SUBSTR(tags, '\[/ (.+?)\]', 1, LEVEL, NULL, 1) AS paths
FROM t connect BY LEVEL <= REGEXP_COUNT(tags, '\[/ (.+?)\]')
) s
Results:
| ALL_PATHS |
|-------------------------------------------------------------------|
| [/home/pc/test/alseko][/home/pc/test/archive][/home/pc/test/logs] |
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.
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)))