Stuck on Oracle SQL query - sql

We have a query that has been leaving me scratching my head for the past week. The query works on one DB, but not another (apologies for the wall of text):
WITH dataset AS
( SELECT COLLECTION_TIMESTAMP,
TARGET_NAME,
METRIC_NAME,
KEY_VALUE,
KEY_VALUE2,
KEY_VALUE3,
COLUMN_LABEL,
MAX(VALUE) AS VALUE
FROM (SELECT TO_CHAR(COLLECTION_TIMESTAMP, 'DD-MM-YYYY HH24:MI:SS') AS COLLECTION_TIMESTAMP,
TARGET_NAME,
METRIC_NAME,
KEY_VALUE,
KEY_VALUE2,
KEY_VALUE3,
COLUMN_LABEL,
VALUE
FROM (SELECT MAX(COLLECTION_TIMESTAMP)
OVER(
PARTITION BY TARGET_NAME,
METRIC_NAME,
KEY_VALUE,
KEY_VALUE2,
KEY_VALUE3,
COLUMN_LABEL
)
max_my_date,
COLLECTION_TIMESTAMP,
TARGET_NAME,
METRIC_NAME,
KEY_VALUE,
KEY_VALUE2,
KEY_VALUE3,
COLUMN_LABEL,
VALUE
FROM MGMT$METRIC_DETAILS
WHERE METRIC_LABEL LIKE 'SOX%')
WHERE COLLECTION_TIMESTAMP = max_my_date)
WHERE METRIC_NAME = 'ME$SOXREP_USERS'
GROUP BY COLLECTION_TIMESTAMP,
TARGET_NAME,
METRIC_NAME,
KEY_VALUE,
kEY_VALUE2,
KEY_VALUE3,
COLUMN_LABEL
ORDER BY TARGET_NAME ASC,
METRIC_NAME ASC,
COLUMN_LABEL ASC)
SELECT (SELECT NVL(amt.AGENT_HOST_NAME, ' ') AS AGENT_HOST_NAME
FROM MGMT$TARGET t LEFT OUTER JOIN MGMT$AGENTS_MONITORING_TARGETS amt ON t.TARGET_GUID = amt.TARGET_GUID
WHERE t.TARGET_NAME = prf.TARGET_NAME)
AS "HOSTNAME",
(SELECT NVL(PROPERTY_VALUE, ' ')
FROM MGMT$TARGET_PROPERTIES
WHERE PROPERTY_NAME = 'IP_address'
AND TARGET_NAME = (SELECT NVL(amt.AGENT_HOST_NAME, ' ') AS AGENT_HOST_NAME
FROM MGMT$TARGET t LEFT OUTER JOIN MGMT$AGENTS_MONITORING_TARGETS amt ON t.TARGET_GUID = amt.TARGET_GUID
WHERE t.TARGET_NAME = prf.TARGET_NAME))
AS "IP ADDRESS",
(SELECT NVL(PROPERTY_VALUE, ' ')
FROM MGMT$TARGET_PROPERTIES
WHERE PROPERTY_NAME = 'DBVersion'
AND TARGET_NAME = prf.TARGET_NAME)
AS "DB VERSION",
(SELECT NVL(PROPERTY_VALUE, ' ')
FROM MGMT$TARGET_PROPERTIES
WHERE PROPERTY_NAME = 'InstanceName'
AND TARGET_NAME = prf.TARGET_NAME)
AS "DB INSTANCE",
'All database accounts' AS DESCRIPTION,
prf.KEY_VALUE AS "USERNAME",
prf.VALUE AS "PROFILE",
acc.VALUE AS "ACCOUNT STATUS",
CAST(TO_TIMESTAMP(created.VALUE, 'YYYY-MM-DD HH24:MI:SS.FF1') AS DATE) AS "CREATED",
CAST(TO_TIMESTAMP(passwd.VALUE, 'YYYY-MM-DD HH24:MI:SS.FF1') AS DATE) AS "LAST_PWD_CHANGED",
d.VALUE AS "DAYS_SINCE_LAST_PWD_CHANGED",
(SELECT NVL(PROPERTY_VALUE, ' ')
FROM MGMT$TARGET_PROPERTIES
WHERE PROPERTY_NAME = 'orcl_gtp_contact'
AND TARGET_NAME = prf.TARGET_NAME)
AS "ENVIRONMENT ROLES",
CAST(TO_TIMESTAMP(dl.VALUE, 'YYYY-MM-DD HH24:MI:SS.FF1') AS DATE) AS "LAST LOGON",
TO_DATE(prf.COLLECTION_TIMESTAMP, 'DD-MM-YYYY HH24:MI:SS') AS "COLLECTION DATE"
FROM dataset prf,
dataset acc,
dataset created,
dataset passwd,
dataset d,
dataset dl
WHERE prf.TARGET_NAME = acc.TARGET_NAME
AND prf.KEY_VALUE = acc.KEY_VALUE
AND prf.TARGET_NAME = created.TARGET_NAME
AND prf.KEY_VALUE = created.KEY_VALUE
AND prf.TARGET_NAME = passwd.TARGET_NAME
AND prf.KEY_VALUE = passwd.KEY_VALUE
AND prf.TARGET_NAME = d.TARGET_NAME
AND prf.KEY_VALUE = d.KEY_VALUE
AND prf.TARGET_NAME = dl.TARGET_NAME
AND prf.KEY_VALUE = dl.KEY_VALUE
AND prf.COLUMN_LABEL = 'PROFILE'
AND acc.COLUMN_LABEL = 'ACCOUNT STATUS'
AND created.COLUMN_LABEL = 'CREATED'
AND passwd.COLUMN_LABEL = 'LAST PWD CHANGE'
AND d.COLUMN_LABEL = 'Days since last pwd change'
AND dl.COLUMN_LABEL = 'LAST LOGON'
ORDER BY 1 ASC,
4,
5 DESC
On one DB, the query runs just fine. On the other DB though, we get the following:
ORA-01427: single-row subquery returns more than one row
01427. 00000 - "single-row subquery returns more than one row"
*Cause:
*Action:
I've been trying to deconstruct the query in an attempt to figure out which portion of the query is causing the issue, but so far I've had no luck. I understand that a subquery which is supposed to return only one row is returning 2 or more, but I'm struggling to figure out which one it is.
My question: Is there an easier way for me to figure out which portion of the SQL query is causing the issue instead of having to try to deconstruct the query and running it bit by bit?

You can wrap your subqueries to check which one gives more than one row, at least for the queries you don't need to check against a value. For example:
SQL> select
2 (select 1 from dual) as result_1,
3 (select 1 from dual connect by level < 10) as result_2
4 from dual;
(select 1 from dual connect by level < 10) as result_2
*
ERROR at line 3:
ORA-01427: single-row subquery returns more than one row
SQL> select
2 (select count(1) from (select 1 from dual)) as check1,
3 (select count(1) from (select 1 from dual connect by level < 10)) as check_2
4 from dual;
CHECK1 CHECK_2
---------- ----------
1 9
Here you know that the issue is in the second query.

Related

I am getting nested exception is java.sql.SQLSyntaxErrorException: ORA-00909: invalid number of arguments

this is my query,
SELECT
MAX(CASE WHEN rownumber='1' THEN DISPOSITON END) AS Dispostion
,MAX(CASE WHEN rownumber='1' THEN SUB_DISPOSITION END) AS SUB_DISPOSITION
,CUST_NAME
,MOBILE_NO
,EMAIL_ID
,LINE_OF_BUSINESS
,PRODUCT_CODE
,REGISTRATION_NUMBER
FROM
(
SELECT
ROW_NUMBER() OVER (PARTITION BY task_id ORDER BY CREATED_DATE ASC) AS rownumber
,a.DISPOSITON
,a.SUB_DISPOSITION
,b.CUST_NAME
,b.MOBILE_NO
,b.EMAIL_ID
,b.LINE_OF_BUSINESS
,b.PRODUCT_CODE
,b.REGISTRATION_NUMBER
FROM CLICK_TO_CALL_AUDIT_LOG a
INNER JOIN IM_DATA b
ON a.task_id=b.task_code
AND UPPER(a.DISPOSITON) = UPPER(:Dis)
AND UPPER(a.SUB_DISPOSITION) in (UPPER(:subDis))
AND a.CREATED_DATE BETWEEN to_timestamp(:before , 'dd-mm-yy hh24:mi:ss')
AND to_timestamp(:current, 'dd-mm-yy hh24:mi:ss')
WHERE b.ALLOCATED_USER IN (:userNameList)
AND b.IS_LATEST = 'Y'
AND b.TASK_STATUS <> 'JUNK LEAD'
AND b.LINE_OF_BUSINESS = :LOB
) R
GROUP BY
CUST_NAME
,MOBILE_NO
,EMAIL_ID
,LINE_OF_BUSINESS
,PRODUCT_CODE
,REGISTRATION_NUMBER;
This:
AND UPPER(a.SUB_DISPOSITION) in (UPPER(:subDis))
should most probably be rewritten so that it represents rows (instead of a, presumably, comma-separated list of values). Something like this:
and upper(a.sub_disposition) in (select trim(regexp_substr(upper(:subDis), '[^,]+', 1, level))
from dual
connect by level <= regexp_count(:subDis, ',') + 1
)
The same goes for
WHERE b.ALLOCATED_USER IN (:userNameList)
I presumed that variables (such as userNameList) looks like e.g. 'Scott,Tiger,Wood,King'. If it looks differently, code should also be adjusted.

Select the first row that meets a condition + 1 row above

I have written a SQL query to return the following results:
Rows returned
However, I need to further 'filter' it down to only the rows highlighted in yellow, because I want the first row that has param_id = 'update_flag' AND param_val = '1'. I also need the row above that, because I need to retrieve the param_val value (202017) that corresponds to param_id = 'period_from' for the same Order Number (156 in this example).
This is my code, I'm getting data from 2 different tables:
SELECT
report1.orderno AS t1_orderno,
report2.orderno AS t2_orderno,
report1.report_name AS t1_reportname,
report2.report_name AS t2_reportname,
report1.variant,
report1.status,
report1.client,
report2.param_id,
report2.param_val
FROM report1
INNER JOIN
report2
ON report1.orderno = report2.orderno
AND report2.param_id IN
(
'period_from',
'update_flag'
)
AND report1.report_name = 'PR28'
AND report1.variant = '20'
AND report1.status = 'T'
AND report1.client = '10'
ORDER BY
report1.orderno DESC
Thanks for any help in advance, it's doing my head in!
WITH num_row AS (
SELECT row_number() OVER (ORDER BY report1.orderno DESC) as nom,
report1.orderno AS t1_orderno,
report2.orderno AS t2_orderno,
report1.report_name AS t1_reportname,
report2.report_name AS t2_reportname,
report1.variant,
report1.status,
report1.client,
report2.param_id,
report2.param_val
FROM report1
INNER JOIN
report2
ON report1.orderno = report2.orderno
AND report2.param_id IN (
'period_from',
'update_flag'
)
AND report1.report_name = 'PR28'
AND report1.variant = '20'
AND report1.status = 'T'
AND report1.client = '10'
)
SELECT * FROM num_row
WHERE nom BETWEEN 1 AND 2

How to create a view from existing table records, but also adding new records that do not exist

I am trying to create a view from an existing views data, but also if there are certain lines that do not exist per part/date combo, then have those lines be created. I have the below query that is showing what I currently have for the particular s_date/part_no combos:
SELECT
s_date,
part_no,
issue_group,
s_level,
qty_filled
FROM
current_view
WHERE
part_no = 'xxxxx'
AND s_date IN (
'201802',
'201803'
)
ORDER BY
s_date,
part_no,
issue_group,
DECODE(s_level, '80', 1, '100', 2, 'Late', 3)
Which produces the below:
I know how to create a view with that data, that's the easy part. But what I'm needing is a line for each issue_group and s_level combo, and if it's a created line, to put 0 as the qty_filled.
Every part_no / s_date combo should have 6 rows that go with it
- issue_group = '1' / s_level = '80'
- issue_group = '1' / s_level = '100'
- issue_group = '1' / s_level = 'Late'
- issue_group = '2/3 ' / s_level = '80'
- issue_group = '2/3 ' / s_level = '100'
- issue_group = '2/3 ' / s_level = 'Late'
So if one of the above combinations already exists for the current s_date/part_no, then it obviously takes the qty_filled info from the current view. If not, a new line is created, and qty_filled = 0. So I'm trying to get it to look like this:
I've only shown 1 part, with a couple dates, just to get the point across. There are 10k+ parts within the table and there will never be more than 1 part/date combo for each of the 6 issue_group/s_level combos.
The idea is to generate the rows using CROSS JOIN and then bring in the extra information with a LEFT JOIN. In Oracle syntax, this looks like:
WITH v as (
SELECT v.*
FROM current_view v
WHERE part_no = 'xxxxx' AND
s_date IN ('201802', '201803')
)
SELECT d.s_date, ig.part_no, ig.issue_group, l.s_level,
COALESCE(v.qty_filled, 0) as qty_filled
FROM (SELECT DISTINCT s_date FROM v) d CROSS JOIN
(SELECT DISTINCT part_no, ISSUE_GROUP FROM v) ig CROSS JOIN
(SELECT '80' as s_level FROM DUAL UNION ALL
SELECT '100' FROM DUAL UNION ALL
SELECT 'LATE' FROM DUAL
) l LEFT JOIN
v
ON v.s_date = d.s_date AND v.part_no = ig.part_no AND
v.issue_group = ig.issue_group AND v.s_level = l.s_level
ORDER BY s_date, part_no, issue_group,
(CASE s_level WHEN '80' THEN 1 WHEN '100' THEN 2 WHEN 'Late' THEN 3 END)
One solution could be to generate a cartesian product of all expected rows using a cartesian product between the (fixed) list of values, and then LEFT JOIN it with current_view.
The following query guarantees that you will get a record for each given s_date/part_no/issue_group/s_level tuple. If no record matches in current_view, the query will display a 0 quantity.
SELECT
sd.s_date,
pn.part_no,
ig.issue_group,
sl.s_level,
COALESCE(cv.qty_filled, 0) qty_filled
FROM
(SELECT '201802' AS s_date UNION SELECT '201803') AS sd
CROSS JOIN (SELECT 'xxxxx' AS part_no) AS pn
CROSS JOIN (SELECT '1' AS issue_group UNION SELECT '2') AS ig
CROSS JOIN (SELECT '80' AS s_level UNION SELECT '100' UNION SELECT 'Late') AS sl
LEFT JOIN current_view cv
ON cv.s_date = sd.s_date
AND cv.part_no = pn.part_no
AND cv.issue_group = ig.issue_group
AND cv.s_level = ig.s_level
ORDER BY
sd.s_date,
pn.part_no,
ig.issue_group,
DECODE(sl.s_level, '80', 1, '100', 2, 'Late', 3)
NB : you did not tag your RDBMS. This should work on most of them, excepted Oracle, where you need to add FROM DUAL to each select in the queries that list the allowed values, like :
(SELECT '201802' AS s_date FROM DUAL UNION SELECT '201803' FROM DUAL) AS sd

How to Select * Where Everything is Distinct Except One Field

I'm trying to pull 6 records using the code below but there are some cases where the information is updated and therefore it is pulling duplicate records.
My code:
SELECT column2, count(*) as 'Count'
FROM ServiceTable p
join HIERARCHY h
on p.LOCATION_CODE = h.LOCATION
where Report_date between '2017-04-01' and '2017-04-30'
and Column1 = 'Issue '
and LOCATION = '8789'
and
( record_code = 'INCIDENT' or
(
SUBMIT_METHOD = 'Web' and
not exists
(
select *
from ServiceTable p2
where p2.record_code = 'INCIDENT'
and p2.incident_id = p.incident_id
)
)
)
The problem is that instead of the six records it is pulling eight. I would just use distinct * but the file_date is different on the duplicate entries:
FILE_DATE Incident_ID Column1 Column2
4/4/17 123 Issue Service - Red
4/4/17 123 Issue Service - Blue
4/5/17 123 Issue Service - Red
4/5/17 123 Issue Service - Blue
The desired output is:
COLUMN2 COUNT
Service - Red 1
Service - Blue 1
Any help would be greatly appreciated! If you need any other info just let me know.
If you turn your original select statement without the aggregation function into a subquery, you can distinct that on your values that are not the changing date, then select a COUNT from there. Don't forget your GROUP BY clause at the end.
SELECT Column2, COUNT(Incident_ID) AS Service_Count
FROM (SELECT DISTINCT Incident_ID, Column1, Column2
FROM ServiceTable p
JOIN HIERARCHY h ON p.LOCATION_CODE = h.LOCATION
WHERE Report_date BETWEEN '2017-04-01' AND '2017-04-30'
AND Column1 = 'Issue '
AND LOCATION = '8789'
AND
( record_code = 'INCIDENT' or
(
SUBMIT_METHOD = 'Web' and
NOT EXISTS
(
SELECT *
FROM ServiceTable p2
WHERE p2.record_code = 'INCIDENT'
AND p2.incident_id = p.incident_id)
)
)
)
GROUP BY Column2
Also, if you are joining tables it is a good practice to fully qualify the field you are selecting. Example: p.Column2, p.Incident_ID, h.LOCATION. That way, even your distinct fields are easier to follow where they came from and how they relate.
Finally, don't forget that COUNT is a reserved word. I modified your alias accordingly.
If you are using an aggregation function (count), you should use group by for the column not in the aggregation function:
SELECT column2, count(*) as 'Count'
FROM ServiceTable p
join HIERARCHY h
on p.LOCATION_CODE = h.LOCATION
where Report_date between '2017-04-01' and '2017-04-30'
and Column1 = 'Issue '
and LOCATION = '8789'
and
( record_code = 'INCIDENT' or
(
SUBMIT_METHOD = 'Web' and
not exists
(
select *
from ServiceTable p2
where p2.record_code = 'INCIDENT'
and p2.incident_id = p.incident_id
)
)
)
group by column2

Joining two SELECT statements using Outer Join with multiple alias

I have a complicated select statement that when it is executed, I give it a date or date range I want and the output comes out. The problem is I don't know how to join the same SQL statement with the first Statement having 1 date range and the second statement having another data range. Example below:
When I execute the select statement, I choose for the Month of November:
EMPLID NAME Current_Gross_Hours(November)
When I execute the select statement again, I choose from January to November:
EMPLID NAME Year_To_Date_Hours(January - November)
What I want:
EMPLID NAME Current_Gross_Hours(November) Year_To_Date_Hours(January - November)
The SQL Select statement runs correctly if execute by themselves. But I don't know how to join them.
Here is the SQL code that I want to write, but I don't know how to write the SQL statement correctly. Any help or direction is greatly appreciated.
(SELECT DISTINCT
SUM("PSA"."AL_HOURS") AS "Current Gross Hours", "PSJ"."EMPLID","PSP"."NAME"
FROM
"PS_JOB" "PSJ", "PS_EMPLOYMENT" "PSE", "PS_PERSONAL_DATA" "PSP", "PS_AL_CHK_HRS_ERN" "PSA"
WHERE
((("PSA"."CHECK_DT" = TO_DATE('2011-11-01', 'YYYY-MM-DD')) AND
("PSJ"."PAYGROUP" = 'SK2') AND
(("PSJ"."EFFSEQ"= (
SELECT MAX("INNERALIAS"."EFFSEQ")
FROM "PS_JOB" INNERALIAS
WHERE "INNERALIAS"."EMPL_RCD_NBR" = "PSJ"."EMPL_RCD_NBR"
AND "INNERALIAS"."EMPLID" = "PSJ"."EMPLID"
AND "INNERALIAS"."EFFDT" = "PSJ"."EFFDT")
AND
"PSJ"."EFFDT" = (
SELECT MAX("INNERALIAS"."EFFDT")
FROM "PS_JOB" INNERALIAS
WHERE "INNERALIAS"."EMPL_RCD_NBR" = "PSJ"."EMPL_RCD_NBR"
AND "INNERALIAS"."EMPLID" = "PSJ"."EMPLID"
AND "INNERALIAS"."EFFDT" <= SYSDATE)))))
AND
("PSJ"."EMPLID" = "PSE"."EMPLID" ) AND ("PSJ"."EMPLID" = "PSP"."EMPLID" ) AND ("PSJ"."FILE_NBR" = "PSA"."FILE_NBR" ) AND ("PSJ"."PAYGROUP" = "PSA"."PAYGROUP" ) AND ("PSE"."EMPLID" = "PSP"."EMPLID" )
GROUP BY
"PSJ"."EMPLID", "PSP"."NAME"
) AS "Q1"
LEFT JOIN
(SELECT DISTINCT
SUM("PSA"."AL_HOURS") AS "YEAR_TO_DATE Gross Hours", "PSJ"."EMPLID"
FROM
"PS_JOB" "PSJ", "PS_EMPLOYMENT" "PSE", "PS_PERSONAL_DATA" "PSP", "PS_AL_CHK_HRS_ERN" "PSA"
WHERE
((("PSA"."CHECK_DT" BETWEEN TO_DATE('2011-01-01', 'YYYY-MM-DD') AND TO_DATE('2011-11-01', 'YYYY-MM-DD')) AND
("PSJ"."PAYGROUP" = 'SK2') AND
(("PSJ"."EFFSEQ"= (
SELECT MAX("INNERALIAS"."EFFSEQ")
FROM "PS_JOB" INNERALIAS
WHERE "INNERALIAS"."EMPL_RCD_NBR" = "PSJ"."EMPL_RCD_NBR"
AND "INNERALIAS"."EMPLID" = "PSJ"."EMPLID"
AND "INNERALIAS"."EFFDT" = "PSJ"."EFFDT")
AND
"PSJ"."EFFDT" = (
SELECT MAX("INNERALIAS"."EFFDT")
FROM "PS_JOB" INNERALIAS
WHERE "INNERALIAS"."EMPL_RCD_NBR" = "PSJ"."EMPL_RCD_NBR"
AND "INNERALIAS"."EMPLID" = "PSJ"."EMPLID"
AND "INNERALIAS"."EFFDT" <= SYSDATE)))))
AND
("PSJ"."EMPLID" = "PSE"."EMPLID" ) AND ("PSJ"."EMPLID" = "PSP"."EMPLID" ) AND ("PSJ"."FILE_NBR" = "PSA"."FILE_NBR" ) AND ("PSJ"."PAYGROUP" = "PSA"."PAYGROUP" ) AND ("PSE"."EMPLID" = "PSP"."EMPLID" )
GROUP BY
"PSJ"."EMPLID"
) AS "Q2"
ON "Q1"."EMPLID"="Q2"."EMPLID"
ORDER BY
"Q1"."NAME"
You are missing a SELECT ... FROM at the start. The AS keyword only works when creating column aliases, not query block aliases. Quotation marks are only necessary for case-sensitive column names - they don't look incorrect in your example but they frequently cause mistakes.
SELECT Q1.NAME, ...
FROM
(
SELECT ...
) Q1
JOIN
(
SELECT ...
) Q2
ON Q1.EMPLID=Q2.EMPLID
ORDER BY Q1.NAME