Oracle unions in the wrong order - oracle

I'm trying to understand why my query is returning things in a different order than I expect.
My query is:
SELECT 'PRINTJOBID', MAX(PRINTJOBID), null, null FROM PRINTJOB
UNION
SELECT 'AUTOID', null, MAX(AUTOID), null FROM PRINTJOBSHELLS
UNION
SELECT 'PROCESSLOGID', null, null, MAX(PROCESSLOGID) FROM PROCESSLOG;
I am expecting it to give me 3 rows, with printjobid at the top, followed by autoid, and then at the bottom should be processlogid. However, when I run the query I get autoid at the top, printjobid in the middle, and processlog at the bottom, like this:
AUTOID null 771426 null
PRINTJOBID 76401 null, null
PROCESSLOGID null null 1218693
I have tried googling about Unions being in the wrong order, and I tried searching questions on SO. I didn't see anything that seemed relevant. Is my understanding of how UNION works faulty? I thought that the query would return the rows in the order I put the select statements. Thank you!

In SQL, you can't rely on a query returning results in any special order unless you explicitly specify it in the query.
Use this:
SELECT title, v1, v2, v3
FROM (
SELECT 'PRINTJOBID' title, MAX(PRINTJOBID) v1, null v2, null v3, 1 AS o
FROM PRINTJOB
UNION ALL
SELECT 'AUTOID' title, null, MAX(AUTOID), null, 2 AS o
FROM PRINTJOBSHELLS
UNION ALL
SELECT 'PROCESSLOGID' title, null, null, MAX(PROCESSLOGID), 3 AS o
FROM PROCESSLOG
)
ORDER BY
o

If you want ordering of results, you need to specify an ORDER BY clause. Otherwise you are relying on implementation-specific behaviour. In this case it is probably lucky you did not get the ordering you hoped for originally, as the behaviour could easily change in future, so you need to explicitly specify ordering if it is important to you.

Related

Alternative Query to Implement Minus Query logic

we are using the below-mentioned minus query logic to find out the non-existing record between the 2 tables, is there an alternative logic that can be used via SQL to achieve the same this is causing performance issues and running for a very long time.
SELECT EMPLID,EMPL_RCD,EFFDT,HR_STATUS,EMPL_STATUS
FROM EDWHRSTG.PS_JOB_FULL_S
WHERE EMPLID = '09762931'
MINUS
SELECT EMPLID,EMPL_RCD,EFFDT,HR_STATUS,EMPL_STATUS
FROM SUODS.PS_JOB_S
WHERE EMPLID = '09762931'
You can try using an OUTER JOIN:
SELECT EMPLID,EMPL_RCD,EFFDT,HR_STATUS,EMPL_STATUS
FROM EDWHRSTG.PS_JOB_FULL_S a
LEFT OUTER JOIN (SELECT EMPLID,EMPL_RCD,EFFDT,HR_STATUS,EMPL_STATUS
FROM SUODS.PS_JOB_S
WHERE EMPLID = '09762931') b
ON b.EMPLID = a.EMPL_ID AND
b.EMPL_RCD = a.EMPL_RCD AND
b.EFFDT = a.EFFDT AND
b.HR_STATUS = a.HR_STATUS AND
b.EMPL_STATUS = a.EMPL_STATUS
WHERE b.EMPLID IS NULL AND
b.EMPL_RCD IS NULL AND
b.EFFDT IS NULL AND
b.HR_STATUS IS NULL AND
b.EMPL_STATUS IS NULL
However, I doubt this will perform any better. Your best option is to add an index on the five fields in play here (EMPL_ID, EMPL_RCD, EFFDT, HR_STATUS, EMPL_STATUS) to both tables, or in other words
CREATE INDEX EDWHRSTG.PS_JOB_FULL_S_1
ON EDWHRSTG.PS_JOB_FULL_S (EMPL_ID, EMPL_RCD, EFFDT, HR_STATUS, EMPL_STATUS);
and
CREATE INDEX SUODS.PS_JOB_S_1
ON SUODS.PS_JOB_S (EMPL_ID, EMPL_RCD, EFFDT, HR_STATUS, EMPL_STATUS);
You need to identify where time is being spent, in order to determine root cause if the performance problem; otherwise it’s just a guess.
An Active SQL Monitor report is your diagnostic tool of choice

can I use `=` sign operator with sub-query instead of `IN`

I am just wondering if use = sign operator with sub-query instead of IN
Is it correct way ? and meet the oracle standard ?
Example
select column_name from my_table_1 where id = (select max(id) from my_table_2);
The Difference is related to the number of rows returned. If you have only one row returned from nested sql you may prefer both = or in operators. But if multiple rows returned from nested query, use in operator.
So, in your sql example you may prefer using any of the operators. Since, max functions returns only one row.
As you are fetching maximum value from subquery to compare with id, Both(= and IN )will work fine. But If you are trying to fetch more than one row then you have to use IN keyword.
If you have 1 result in sub query you are fine with using = sign, except when data type is wrong, for example , checking with same data type of dummy VARCHAR2(1)
select * from dual where 'X' = (select max(dual.dummy) from dual);
Is similar to using in (also same explain plain)
select * from dual where 'X' in (select max(dual.dummy) from dual);
But checking with different/wrong data type will result with exception ORA-01722 Invalid number
select * from dual where 1 =(select max(dual.dummy) from dual);

How to get result from two queries in same output window?

I need output from the below two queries simultaneously in one output window.
QUERY 1
SELECT C.SERVICENAME, C.SERVICEID , B.SOAPIN, B.SOAPOUT, A.TIMESTAMP
FROM Schema1.LG_LOGENTRIES A, Schema1.LG_SOAPREQUESTS B, Schema1.CFG_SOAPSERVICES C
WHERE B.SERVICEID =C.SERVICEID AND
C.SERVICENAME <>'UploadAndPrepareDocumentEx1__sdweb_services_preload' AND
A.ID=B.LOGENTRYID AND B.TIMESTAMP BETWEEN TO_DATE('02/01/2018 11:55:00','dd/mm/yyyy hh24:mi:ss')
AND TO_DATE('02/01/2018 12:03:59','dd/mm/yyyy hh24:mi:ss') AND A.USERID IN (SELECT ID FROM Schema1.CFG_USERS
WHERE NAME=UPPER(TO_CHAR('CGBXGVSG')));
Query 2
SELECT B.JSONIN, B.JSONOUT, A.TIMESTAMP, B.EVENT_MESSAGE, A.PROCESSID, A.status, A.SERVERNAME
FROM Schema1.LG_LOGENTRIES A, Schema1.LG_EVENT B
WHERE B.EVENT_MESSAGE NOT IN ('getFileImage','submitBase64','loadDocumentToSign','getRefData') AND
A.ID=B.LOG_ENTRYID AND B.TIMESTAMP BETWEEN TO_DATE('31/12/2017 13:43:00','dd/mm/yyyy hh24:mi:ss')
AND TO_DATE('31/12/2017 13:53:59','dd/mm/yyyy hh24:mi:ss') AND A.USERID IN (SELECT ID FROM Schema1.CFG_USERS
WHERE NAME=UPPER(TO_CHAR('CTHX8Y2G')));
Run with F5 - you'll get both queries' output in the script panel.
I talk about how this differs here
UNION might be one option, but you'll have to
uniform both column lists (i.e. they have to return the same number of columns which have to be of the same data type), which means that you'd have to add certain NULL columns to both queries
include additional identifier so that you'd know which SELECT returned which values
If you wanted to have them side-by-side, huh, that's not that easy. Thinking loudly: you'd have to have a column that joins those values. Those SELECTs would be inline views. You'd use an aggregate function (such as MAX) along with a DECODE (or CASE) to select values from both queries. Shortly: too much pain.
Now, why do you want to do that? What's wrong with two separate windows, placed side by side?
[EDIT] Showing example of how UNION might look like
select c.servicename, c.serviceid, b.soapin, b.soapout, a.timestamp, to_char(null), to_char(null), to_char(null) , to_number(null), to_char(null), to_char(null)
from ... the rest of your 1st query
union
select null , null , null , null , a.timestamp, b.jsonin , b.jsonout , b.event_message, a.processid , a.status , a.servername
from ... the rest of your 2nd query

Oracle dba_tab_cols query

Hi is it possible to retrieve the primary key and unique key using the dba_tab_cols query?
Is there any query that allows me to retrieve all of the following fields?
Column Name
Data Type
Primary Key
Null/Not Null
Unique Key
Default Value
Extra
Both primary and unique keys can span more than one column, so they wouldn't belong in dba_tab_columns. You'd need to look at dba_constraints and dba_cons_columns to get that information.
This is a starting point, maybe:
select owner, table_name, column_name, data_type, primary_key,
nullable, unique_key, data_default
from (
select dtc.owner, dtc.table_name, dtc.column_id, dtc.column_name,
dtc.data_type, dtc.nullable, dtc.data_default,
case when dc.constraint_type = 'P' and dcc.column_name = dtc.column_name
then dc.constraint_name end as primary_key,
case when dc.constraint_type = 'U' and dcc.column_name = dtc.column_name
then dc.constraint_name end as unique_key,
row_number() over (partition by dtc.owner, dtc.table_name, dtc.column_id
order by null) as rn
from dba_tab_columns dtc
left join dba_constraints dc
on dc.owner = dtc.owner
and dc.table_name = dtc.table_name
and dc.constraint_type in ('P', 'U')
left join dba_cons_columns dcc
on dcc.owner = dc.owner
and dcc.constraint_name = dc.constraint_name
and dcc.table_name = dc.table_name
and dcc.column_name = dtc.column_name
where dtc.owner = '<owner>'
and dtc.table_name = '<table_name>'
)
where rn = 1
order by owner, table_name, column_id;
I've done this with a subquery that generates a row_number value because you'd get duplicates for a table with more than one constraint; and because you want the default value, which is a long (column data_default), you can't use distinct or group by. It feels a bit inelegant, but I'm sure you can work on it to get what you need.
It's also possible to have a check constraint that replicates the not null version, though it isn't advisable. And a unique index won't show up as a unique constraint, so you might want to look for one of those too, via dba_indexes and dba_ind_columns. An index used to back up a unique constrain will appear in both, though.
You could look at dbms_metadata.get_ddl to get this information too, depending on what you intend to do with it. I'm not sure why this would be useful, other than to try to recreate the schema elsewhere, and there are better tools for doing that.

NOT IN query... odd results

I need a list of users in one database that are not listed as the new_user_id in another. There are 112,815 matching users in both databases; user_id is the key in all queries tables.
Query #1 works, and gives me 111,327 users who are NOT referenced as a new_user_Id. But it requires querying the same data twice.
-- 111,327 GSU users are NOT listed as a CSS new user
-- 1,488 GSU users ARE listed as a new user in CSS
--
select count(gup.user_id)
from gsu.user_profile gup
join (select cud.user_id, cud.new_user_id, cud.user_type_code
from css.user_desc cud) cudsubq
on gup.user_id = cudsubq.user_id
where gup.user_id not in (select cud.new_user_id
from css.user_desc cud
where cud.new_user_id is not null);
Query #2 would be perfect... and I'm actually surprised that it's syntactically accepted. But it gives me a result that makes no sense.
-- This gives me 1,505 users... I've checked, and they are not
-- referenced as new_user_ids in CSS, but I don't know why the ones
-- that were excluded were excluded.
--
-- Where are the missing 109,822, and whatexcluded them?
--
select count(gup.user_id)
from gsu.user_profile gup
join (select cud.user_id, cud.new_user_id, cud.user_type_code
from css.user_desc cud) cudsubq
on gup.user_id = cudsubq.user_id
where gup.user_id not in (cudsubq.new_user_id);
What exactly is the where clause in the second query doing, and why is it excluding 109,822 records from the results?
Note The above query is a simplification of what I'm really after. There are other/better ways to do the above queries... they're just representative of the part of the query that's giving me problems.
Read this: http://asktom.oracle.com/pls/asktom/f?p=100:11:0::NO::P11_QUESTION_ID:442029737684
For what I understand, your cudsubq.new_user_id can be NULL even though both tables are joined by user_id, so, you won't get results using the NOT IN operator when the subset contains NULL values . Consider the example in the article:
select * from dual where dummy not in ( NULL )
This returns no records. Try using the NOT EXISTS operator or just another kind of join. Here is a good source: http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html
And what you need is the fourth example:
SELECT COUNT(descr.user_id)
FROM
user_profile prof
LEFT OUTER JOIN user_desc descr
ON prof.user_id = descr.user_id
WHERE descr.new_user_id IS NULL
OR descr.new_user_id != prof.user_id
Second query is semantically different. In this case
where gup.user_id not in (cudsubq.new_user_id)
cudsubq.new_user_id is treated as expression (doc: IN condition), not as a subquery, thus the whole clause is basically equivalent to
where gup.user_id != cudsubq.new_user_id
So, in your first query, you're literally asking "show me all users in GUP, who also have entries in CSS and their GUP.ID is not matching ANY NOT NULL NEW_ID in CSS ".
However, the second query is "show me all users in GUP, who also have entries in CSS and their GUP.ID is not equal to their RESPECTIVE NULLABLE (no is not null clause, remember?) CSS.NEW_ID value".
And any (not) in (or equality/inequality) checks with nulls don't actually work.
12:07:54 SYSTEM#oars_sandbox> select * from dual where 1 not in (null, 2, 3, 4);
no rows selected
Elapsed: 00:00:00.00
This is where you lose your rows. I would probably rewrite your second query's where clause as
where cudsubq.new_user_id is null, assuming that non-matching users have null new_user_id.
Your second select compares gup.user_id with cud.new_user_id on current joining record. You can rewrite the query to get the same result
select count(gup.user_id)
from gsu.user_profile gup
join (select cud.user_id, cud.new_user_id, cud.user_type_code
from css.user_desc cud) cudsubq
on gup.user_id = cudsubq.user_id
where gup.user_id != cud.new_user_id or cud.new_user_id is null;
You mentioned you compare list of user in one database with a list of users in another. So you need to query data twice and you don't query the same data. Maybe you can use "minus" operator to avoid using "in"
select count(gup.user_id)
from gsu.user_profile gup
join (select cud.user_id from css.user_desc cud
minus
select cud.new_user_id from css.user_desc cud) cudsubq
on gup.user_id = cudsubq.user_id;
You want new_user_id's from table gup that don't match any new_user_id on table cud, right? It sounds like a job for a left join:
SELECT count(gup.user_id)
FROM gsu.user_profile gup LEFT JOIN css.user_desc cud
ON gup.user_id = cud.new_user_id
WHERE cud.new_user_id is NULL
The join keeps all rows of gup, matching them with a new_user_id if possible. The WHERE condition keeps only the rows that have no matching row in cud.
(Apologies if you know this already and you're only interested in the behavior of the not in query)

Resources