How to write a table literal in Oracle? - oracle

A table with one column and one row can be created with:
select 'create' as col from dual;
This can be used to build table joins:
with
a as (select 'create' as ac from dual),
b as (select 'delete' as bc from dual)
select * from a left outer join b on (ac = bc);
Now I would like to have two rows. I did it in this way:
select 'create' as col from dual
union
select 'delete' as col from dual;
But is there a more compact notation for this? I tried
select ('create', 'delete') as col from dual;
but it does not work.

You can use collection type and TABLE operator, for example (works in Oracle 10g):
SQL> SELECT column_value FROM TABLE(SYS.ODCIVARCHAR2LIST('abc', 'def', 'ghi'));
COLUMN_VALUE
--------------------------------------------------------------------------------
abc
def
ghi

A couple of ways to generate rows. You could use rownum against a table with a larger number of rows:
SELECT roWnum AS a
FROM user_objects
WHERE rownum <= 3
You could use a hierarchical query:
SELECT level AS a
FROM dual
CONNECT BY LEVEL <= 3
EDIT: change int sequence to alpha sequence:
SELECT CHR( ASCII('a') + level - 1 )
FROM dual
CONNECT BY LEVEL <= 3

Related

Oracle How to make SELECT INSIDE A SELECT work?

Just wondering why the following select isn't working:
SELECT
A.FIELD1
, (SELECT PCN FROM (select B.PRIORITY, B.PCN
from
TABLE2 B
WHERE B.CUST= A.CUST
ORDER BY B.PRIORITY)
WHERE ROWNUM = 1) AS PCN
FROM TABLE1 A;
ERROR at line 2: ORA-00904: "A"."CUST": invalid identifier
Important to mention:
TABLE1 has as fields FIELD1, CUST.
TABLE2 has as fields PCN, PRIORITY, CUST.
Thanks in advance.
Your query shouldn't give you that error message, on when you remove the outer qiery this would happen
CREATE tABLE TABLE1 (FIELD1 int, CUST int)
INSERT INTO TABLE1 VALUES(1,1)
1 rows affected
CREATE TABLE TABLE2 (PCN int, PRIORITY int, CUST int)
INSERT INTO TABLE2 VALUES (1,1,1)
1 rows affected
SELECT
A.FIELD1
, (SELECT PCN FROM (select B.PRIORITY, B.PCN
from
TABLE2 B
WHERE B.CUST= A.CUST
ORDER BY B.PRIORITY)
WHERE ROWNUM = 1) AS PCN
FROM TABLE1 A;
FIELD1
PCN
1
1
fiddle
You can't nest inline selects (more than one level) without losing the ability of the inner nested selects being able to reference the parent block. So your query on TABLE2 cannot see the columns from TABLE1 because of this nesting.
Try this:
SELECT a.field1,
pcn.pcn
FROM table1 a,
(SELECT b.cust,
b.priority,
b.pcn,
ROW_NUMBER() OVER (PARTITION BY b.cust ORDER BY b.priority DESC) seq
FROM table2 b) pcn
WHERE a.cust = pcn.cust(+)
AND pcn.seq(+) = 1
That will work well for report queries. If you end up adding a filter on a specific customer, then you would be better off using OUTER APPLY if you have a recent-enough version of Oracle that supports that.
You could try this:
SELECT
A.FIELD1
, (SELECT B.PCN
from
TABLE2 B
WHERE B.CUST= A.CUST
ORDER BY B.PRIORITY
FETCH FIRST 1 ROWS ONLY) AS PCN
FROM TABLE1 A;
FETCH FIRST 1 ROWS ONLY gets you the first ordered record. Works on 12c and up and supports nesting, and no 2nd subquery needed.
Yet another option might be a CTE.
Sample data:
SQL> with
2 table1 (field1, cust) as
3 (select 1, 100 from dual union all
4 select 2, 200 from dual
5 ),
6 table2 (pcn, priority, cust) as
7 (select 10, 1, 100 from dual union all
8 select 20, 2, 100 from dual union all
9 select 30, 1, 200 from dual
10 ),
Query begins here. Rank rows by priority, and then fetch the ones that rank as the highest (line #20):
11 temp as
12 (select a.field1,
13 b.pcn,
14 rank() over (partition by a.field1 order by b.priority desc) rnk
15 from table1 a join table2 b on a.cust = b.cust
16 )
17 select field1,
18 pcn
19 from temp
20 where rnk = 1;
FIELD1 PCN
---------- ----------
1 20
2 30
SQL>
You may use first aggregate function to achieve the same (assuming that you have completely deterministic order by) functionality without nested subquery:
select
a.field1
, (
select max(b.pcn) keep(dense_rank first order by b.priority)
from table2 b
where b.cust = a.cust
) as pcn
from table1 a
which for this sample data
insert into table1 values(1,1);
insert into table1 values(2,2);
insert into table2 values(1,1,1);
insert into table2 values(2,2,1)
returns
FIELD1
PCN
1
1
2
(null)
SQL fiddle

Oracle SQL -- Finding count of rows that match date maximum in table

I am trying to use a query to return the count from rows such that the date of the rows matches the maximum date for that column in the table.
Oracle SQL: version 11.2:
The following syntax would seem to be correct (to me), and it compiles and runs. However, instead of returning JUST the count for the maximum, it returns several counts more or less like the "HAIVNG" clause wasn't there.
Select ourDate, Count(1) as OUR_COUNT
from schema1.table1
group by ourDate
HAVING ourDate = max(ourDate) ;
How can this be fixed, please?
You can use:
SELECT MAX(ourDate) AS ourDate,
COUNT(*) KEEP (DENSE_RANK LAST ORDER BY ourDate) AS ourCount
FROM schema1.table1
or:
SELECT ourDate,
COUNT(*) AS our_count
FROM (
SELECT ourDate,
RANK() OVER (ORDER BY ourDate DESC) AS rnk
FROM schema1.table1
)
WHERE rnk = 1
GROUP BY ourDate
Which, for the sample data:
CREATE TABLE table1 (ourDate) AS
SELECT SYSDATE FROM DUAL CONNECT BY LEVEL <= 5 UNION ALL
SELECT SYSDATE - 1 FROM DUAL;
Both output:
OURDATE
OUR_COUNT
2022-06-28 13:35:01
5
db<>fiddle here
I don't know if I understand what you want. Try this:
Select x.ourDate, Count(1) as OUR_COUNT
from schema1.table1 x
where x.ourDate = (select max(y.ourDate) from schema1.table1 y)
group by x.ourDate
One option is to use a subquery which fetches maximum date:
select ourdate, count(*)
from table1
where ourdate = (select max(ourdate)
from table1)
group by ourdate;
Or, a more modern approach (if your database version supports it; 11g doesn't, though):
select ourdate, count(*)
from table1
group by ourdate
order by ourdate desc
fetch first 1 rows only;
You can use this SQL query:
select MAX(ourDate),COUNT(1) as OUR_COUNT
from schema1.table1
where ourDate = (select MAX(ourDate) from schema1.table1)
group by ourDate;

Inserting from a SELECT but changing one column in the middle?

Wondering if there is a way to insert a row into a table from another, with exception of one column in the middle without specifying all the column name? I have 128 columns in the table.
I created a view to store the original records.
CREATE VIEW V_TXN_STG AS
SELECT * FROM TXN_STG;
In table TXN_STG, only one column BRN_CODE is changing.
Something like this doesn't work, because the column is not on the last, but somewhere middle of table structure.
INSERT INTO TXN_STG
SELECT v.*, 'BRN-001' AS BRN_CODE
FROM V_TXN_STG v;
I don't believe that this is possible without explicitly specifying the columns in your select.
first you have to get the columns:
SELECT listagg(column_name, ',') within group (order by column_name) columns
FROM all_tab_columns
WHERE table_name = 'AAA' --Table to insert too
and column_name <> 'B' -- column name you want to exclude
GROUP BY table_name;
Then insert that result on the first row:
insert into aaa(A,C) -- A,C is my result from above,I have excluded column B
select *
from (select 'a' A,'q' AMOUNT,'c' C from dual union all -- my sample data
select 'a','a','c' from dual union all
select 'a','b','c' from dual union all
select 'a','c','c' from dual union all
select 'a','d','c' from dual )
pivot
(
max(1)
for (AMOUNT) -- the column you want to remove from the sample data
IN ()
)
where 1=1
order by A;

ORACLE SQL REPEAT SAME QUERY

It seems that I haven't been clear enough.
The query that seems to work is:
Select ((Select count (table1.id) from table1 where table1.code=2 and table1.name=5) as ‘name5’,
(Select count (table1.id) from table1 where table1.code=2 and table1.name=7) as ‘name7’)
From table.1;
union
Select ((Select count (table1.id) from table1 where table1.code=5 and table1.name=5) as ‘name5’,
(Select count (table1.id) from table1 where table1.code=5 and table1.name=7) as ‘name7’)
From table.1;
union
Select ((Select count (table1.id) from table1 where table1.code=15 and table1.name=5) as ‘name5’,
(Select count (table1.id) from table1 where table1.code=15 and table1.name=7) as ‘name7’)
From table.1;
….
Which gets an outcome like this:
name5 name7
52 47
42 84
61 11
My problem is that the table1.code has a thousand and more values other than 2,5 and 15 and I can not repeat a union statement for so many times.
Well it seems like you actually just want to group by the values in the code column, and you can use IN or EXISTS
select count(table1.id) as theCount, table1.code as theCode
from table1 where table1.code in ('code a','code b', 'etc...')
group by table1.code;
the output would be
theCount||theCode
code a || 8074
code b || 34
etc... || 9575
or something like that but with non notional numbers for counts
HTH
You could try to list all values in a nested select listing integers from a to your value, e.g. 100, like that:
select count table1.id from table1 one where table1.code in (
select rownum from all_objects where rownum < 100
);
or if you don't want to start at "1":
select count table1.id from table1 one where table1.code in (
select rownum n from dual connect by level 10 where n>3
);

How to handle singe-row subquery returns more than one row error with case statement

I am building an Oracle query which has a case statement involved.
SELECT
CASE WHEN
((SELECT agent_or_group_id from trans_slot where slot_id =
(SELECT slot_id from trans_slot where slot_alias = 'PP' and measure_expiration > sysdate)) > 0)
/*The below subquery returns 1 row*/
THEN (SELECT agent_or_group_id from trans_slot where slot_id =
(SELECT slot_id from trans_slot where slot_alias = 'PP' and measure_expiration > sysdate))
ELSE
/* The below subquery returns 2 rows*/
(SELECT child_agent_id FROM agent_object_group_member WHERE parent_agent_id IN
(SELECT agent_or_group_id FROM trans_slot WHERE slot_id IN
(SELECT slot_id FROM trans_slot WHERE slot_alias = 'PP' AND measure_expiration > sysdate)
)
)
END
"Agent_ID" from DUAL;
When the run the subqueries independent they run fine. But running the whole query returns
ORA-01427: single-row subquery returns more than one row
01427. 00000 - "single-row subquery returns more than one row"
*Cause:
*Action:
If I understand what you're after, you can't do it like that. You'll need to use a left outer join and then chose the column value you want to display.
Here's a simplified example based on the SQL you provided:
WITH t1 AS (SELECT 1 ID FROM dual UNION ALL
SELECT 0 ID FROM dual UNION ALL
SELECT 2 ID FROM dual),
t2 AS (SELECT 10 child_id, 0 parent_id FROM dual UNION ALL
SELECT 20 child_id, 0 parent_id FROM dual UNION ALL
SELECT 30 child_id, 1 parent_id FROM dual UNION ALL
SELECT 40 child_id, 2 parent_id FROM dual)
---- end of mimicking two tables with the sample data in them. See the query below:
SELECT COALESCE(t2.child_id, t1.id) ID
FROM t1
LEFT OUTER JOIN t2 ON (t1.id = t2.parent_id AND t1.id = 0);
ID
----------
10
20
2
1
Here, I have used the t1 and t2 subqueries to mimic the output you'd get from your main subqueries in your original query.
Then we outer join t2 to t1 only where the t1.id = 0. By doing this, you can then simply choose the t2.child_id value if it exists, otherwise use the t1.id value.
(I realise that in your example, the t1 equivalent subquery would only generate 1 row, based on what you said, but I've included 3 rows so that you can see what the results would be based on the different ids.)
ETA:
In your case, the t1 subquery in my example above would be:
SELECT agent_or_group_id
from trans_slot
where slot_id = (SELECT slot_id
from trans_slot
where slot_alias = 'PP'
and measure_expiration > sysdate)
and the t2 subquery would be:
SELECT child_agent_id
FROM agent_object_group_member
WHERE parent_agent_id IN (SELECT agent_or_group_id
FROM trans_slot
WHERE slot_id IN (SELECT slot_id
FROM trans_slot
WHERE slot_alias = 'PP'
AND measure_expiration > sysdate))

Resources