Single-row subquery returns more than one row error for COUNT() function - oracle

I am trying to understand why this would throw an error-
select count((select 1 br from dual
union
select 2 br from dual))
from dual;
ORA-01427: single-row subquery returns more than one row
But This won't-
select count(br)
from (select 1 br from dual
union
select 2 br from dual);
BR
----------
2

Take a look at the syntax of COUNT function here:
This function accepts the expression as it's argument.
Now take a look at the definition of the expression here.
An expression is a combination of one or more values, operators, and SQL functions that evaluates to a value.
A value - means single, one, scalar, individual, simple, atomic value
Not many values like a vector, an array, a table or a resultset.
Just one value, like 2 or 10.
The expression can have many forms, could be even a subquery, but SQL restricts this subquery to a special form called a scalar subquery, see this:
expression :=
A scalar subquery is defined here
A scalar subquery expression is a subquery that returns exactly one column value from one row.
Since the subquery in your query returns two rows, it doesn't conforms to the definition of the scalar subquery, and this is the reason of this error:
ORA-01427: single-row subquery returns more than one row
EDIT: Question #2: But isn't count supposed to count the number of rows in a result set?
Yes, of course. Count is one of aggregate functions.
See this chapter: http://docs.oracle.com/cd/E11882_01/server.112/e26088/functions003.htm#SQLRF20035
and also examples here: http://docs.oracle.com/cd/E11882_01/server.112/e26088/statements_10002.htm#SQLRF55380
aggregate functions return a single result row based on groups of rows, rather than on single rows. Aggregate functions can appear in select lists and in ORDER BY and HAVING clauses. They are commonly used with the GROUP BY clause in a SELECT statement, where Oracle Database divides the rows of a queried table or view into groups. In a query containing a GROUP BY clause, the elements of the select list can be aggregate functions, GROUP BY expressions, constants, or expressions involving one of these. Oracle applies the aggregate functions to each group of rows and returns a single result row for each group.
If you omit the GROUP BY clause, then Oracle applies aggregate functions in the select list to all the rows in the queried table or view. You use aggregate functions in the HAVING clause to eliminate groups from the output based on the results of the aggregate functions, rather than on the values of the individual rows of the queried table or view.
In simple words, aggregate functions can only be used in queries with GROUP BY and HAVING clauses, like this:
SELECT count( x )
FROM table
......
GROUP BY yyy
The special case is when you omit GROUP BY:
If you omit the GROUP BY clause, then Oracle applies aggregate functions in the select list to all the rows in the queried table or view.
SELECT count( x )
FROM table
But still, in these queries, count can accept only the expression (the scalar subquery) or * as it's argument.

Related

I tested in my SQL Developer one case about "Subquery in Order By"

I have question about "Subquery in Order by clause". The below request returns the error. Is it means that Subquery in Order by clause must be scalar?
select *
from employees
order by (select * from employees where first_name ='Steven' and last_name='King');
Error:
ORA-00913: too many values
00913. 00000 - "too many values"
Yes, it means that if you use a subquery in ORDER BY it must be scalar.
With select * your subquery returns multiple columns and the DBMS would not know which of these to use for the sorting. And if you selected one column only, you would still have to make sure you only select one row of course. (The difference is that Oracle sees the too-many-columns problem immediately, but detect too many rows only when fetching the data.)
This would be allowed:
select * from employees
order by (select birthdate from employees where employee_id = 12345);
This is a scalar query, because it returns only one value (one column, one row). But of course this still makes as little sense as your original query, because the subquery result is independent from the main query, i.e. it returns the same value for every row in the table and thus no sorting takes effect.
A last remark: A subquery in ORDER BY makes very seldomly sense, because that would mean you order by something you don't display. The exception is when looking up a sortkey. E.g.:
select *
from products p
where type = 'shirt' and color = 'blue' and size in ('S', 'M', 'L', 'XL')
order by (select sortkey from sizes s where s.size = p.size);
It means that valid options for ORDER BY clause can be
expression,
position or
column alias
A subquery is neither of these.

Understanding the behavior of dbms_random.value in where clause

I have a table with two columns(Using oracle 11g database) : Country, IndexNumber. Table contains 10 rows(10 different cities and with its unique index number.)
For example:
Country IndexNUmber
India 1
Australia 2
. .
. .
. .
. .
US 10
Now i want to fetch a random row from above table by generating random number using dbms_random.value(1,10). To achieve that i am using below query:
select * from tab_name where indexnumber = dbms_random.value(1,10);
I am not able to understand the output of this query as some time it is fetching one row, some time zero rows and some time more that one row.
Can someone please make me understand how oracle is evaluating this query.
Thanks
Ankit
Since dbms_random.value is a nondeterministic PL/SQL function, it will be called once for each row evaluated by the query.
The function might return 4 when evaluating the first row, then it might return 8 on the second row, etc.
To compare each row to a single random number, you can turn the function call into a scalar subquery, e.g.:
select * from tab_name where indexnumber = (select dbms_random.value(1,10) from dual);
Since the subquery is not correlated to the main query, Oracle will execute it only once (for the first row returned from the table) and remember the result for all subsequent rows. In particular, if a suitable index is on indexnumber the query will be able to use it more efficiently since it knows it is probing for a single value.
When you run your original query:
select * from tab_name where indexnumber = dbms_random.value(1,10);
it appears that the call to dbms_random is happening for each record's where clause. In other words, there is a chance that every record in your table might be returned if the random number chosen happen to match the index for every record. If you want to retrieve a single random record, then follow this pattern:
select *
from
( select * from tab_name order by DBMS_RANDOM.VALUE )
where rownum < 2;

Count with WHERE clause from two tables in Oracle?

I have two tables, one called STUDENTS and the other CLASSES. I have to select all the students that are from the same class of one student, and this student has his own number id, and through this number id that I have to select everything.
TABLE STUDENTS
nr_rgm
nm_name
nm_father
nm_mother
dt_birth
id_sex
TABLE CLASSES
cd_class
nr_schoolyear
cd_school
cd_degree
nr_series
cd_class
cd_period
So I tried something like that :
SELECT count(*) FROM students, classes WHERE id_sex = 'M' AND
cd_class = (SELECT cd_class FROM classes WHERE nr_rgm = '12150');
But then it points an error, and the error is the follow :
single-row subquery returns more than one row
So, how can I make this work ?
you should use "in" and not "=" when applying subselects.
I think what you really would want to do is to simply join the two tables together rather than issuing a sub select:
SELECT count(*)
FROM students s, classes c
WHERE s.id_sex = 'M' AND c.nr_rgm = '12150' AND s.cd_class = c.cd_class;
This way you just tell the database: Please count all the occurrences where my students.id_sex = 'M' and my classes.nr_rgm = '12150' AND all found studends.cd_class match those of my classes.cd_class.
The reason why your statement above fails is because the ordinary = operation, when not used in a join, will only expect one single value, like you do with s.id_sex='M' while your statement returns multiple values. To cope with that you have to use the IN operator which operates on lists.
However, you can and will achieve the very same with just joining the two tables together, and it will be much more efficient on bigger data sets.
One more note to the example above. If classes.nr_rgm is a field of data type NUMBER, don't use the ' around the value 12150 as it will lead to implicit type conversion. With other words, '12150' is a string and will have to be converted to NUMBER first before doing a comparison.

Comparing Similar Hive Tables

I have two hive tables (t1 and t2) that I would like to compare. The second table has 5 additional columns that are not in the first table. Other than the five disjoint fields, the two tables should be identical. I am trying to write a query to check this. Here is what I have so far:
SELECT * FROM t1
UNION ALL
select * from t2
GROUP BY some_value
HAVING count(*) == 2
If the tables are identical, this should return 0 records. However, since the second table contains 5 extra fields, I need to change the second select statement to reflect this. There are almost 60 column names so I would really hate to write it like this:
SELECT * FROM t1
UNION ALL
select field1, field2, field3,...,fieldn from t2
GROUP BY some_value
HAVING count(*) == 2
I have looked around and I know there is no select * EXCEPT syntax, but is there a way to do this query without having to explicity name each column that I want included in the final result?
You should have used UNION DISTINCT for the logic you are applying.
However, the number and names of columns returned by each select_statement have to be the same otherwise a schema error is thrown.
You could have a look at this Python program that handles such comparisons of Hive tables (comparing all the rows and all the columns), and would show you in a webpage the differences that might appear: https://github.com/bolcom/hive_compared_bq
To skip the 5 extra fields, you could use the "--ignore-columns" option.

Oracle Error : Maximum number of expressions in a list is 1000

I am working in C#.Net and Oracle. i am passing a string to a query. i had used this code for concating all the item id's
List<string> listRetID = new List<string>();
foreach (DataRow row in dtNew.Rows)
{
listRetID.Add(row[3].ToString());
}
This concatination goes above 10,000. so i am getting the error message like this..
ORA-01795: maximum number of expressions in a list is 1000
How to fix this..
The documentation states:
A comma-delimited list of expressions can contain no more than 1000
expressions. A comma-delimited list of sets of expressions can contain
any number of sets, but each set can contain no more than 1000
expressions.
Presumably you're using this string as the contents of in IN (...) restriction, in which case there isn't really anything you can do - this just won't work. A common way to work around this is to generate a dummy table as a subquery or common table expression (CTE) and joining to that, but I'm not sure how you'd translate your List - possibly similar to whatever you're doing with your IN clause. You'd want to end up with your query looking something like:
with tmp_tab as (
select <val1 from list> as val from dual
union all select <val2 from list from dual
union all select <val3 from list from dual
...
)
select <something>
from <your table> yt
join tmp_tab tt on yt.<field> = tt.val
But that requires generating the entire (huge) query including the CTE each time you run it, and there's no opportunity to use bind variables.
You might find something like this approach more palatable.
You can have 10 lists of 1000 items instead of 1 list of 10000 items.
WHERE some_column IN (1,2,...,1000)
OR some_column IN (1001,1002,...2000) -- etc.
Not a C# guy but I would just split the list listRetID in multiple lists or create a list of lists
Then loop through that list of lists and perform the query on each element of the list.
What is the intent of your query?
It looks like you are selecting rows that have some column equal to the 3rd column of one of the records of some query.
The correct way of doing this is either an SQL join or a subquery. There is absolutely no need to bring this into C# code. For example, using a subquery you can write something like this:
SELECT *
FROM atable
WHERE afield IN (
SELECT field3
FROM someothertable)

Resources