Execute both the query and the COUNT of the query - sql

I'm trying to build a query or a PL/SQL code to both execute a query, as well as return the number of results of this query.
Is this possible in a single query? Right now I feel like I'm being very wasteful: I first wrap the query in a COUNT (without ORDER BY) and then run the same query again (without the COUNT). A difference of a few seconds will probably not change the total number of rows, I can live with that.
The DB I'm using is Oracle Enterprise 12.2

An easy SQL way:
a test table:
create table testTable(a, b, c) as (
select 1, 'one', 'XX' from dual UNION ALL
select 2, 'two', 'YY' from dual UNION ALL
select 3, 'three', 'ZZ' from dual
)
A simple query:
select a, b, c
from testTable
A B C
---------- ----- --
1 one XX
2 two YY
3 three ZZ
3 rows selected.
The query with the number of records :
select a, b, c, count(*) over (partition by 1) as count
from testTable
A B C COUNT
---------- ----- -- ----------
1 one XX 3
2 two YY 3
3 three ZZ 3
3 rows selected.

You can try to do something like:
WITH test_query
AS (SELECT LEVEL just_a_number
FROM dual
CONNECT BY level < 101)
SELECT just_a_number
, COUNT(just_a_number) OVER (ORDER BY 1) total_count
FROM test_query;
Where COUNT(just_a_number) OVER (ORDER BY 1) will return total number of rows fetched in each row. Note that it may slow down the query.

Typically, when I do something like this, I create a stored procedure that returns 2 values. The first would be the result set as a REF CURSOR, and the other a number(12,0) returning the count. Both would of course require separate queries, but since it is in a single stored procedure, it is only one database connection and command being executed.
You are of course right for having your COUNT query forgo the ORDER BY clause.
To answer your question, you are not being wasteful per se. This is common practice in enterprise software.

Related

Oracle SQL - SUM aggregate not working in LEVEL

I have this query where it has an output when using count but no output when using sum
select to_char(counter)
from (select level counter from dual connect by level <= 1000000)
where counter =
(select sum(a.amount) from table a)
I'm wondering it's because the query only supports outputs of whole numbers? I am expecting to have outputs with decimals.
I am using this as a table value set in Oracle HCM, if anyone's familiar with that. It's why I don't have the aggregate in the SELECT statement as it doesn't support it.
Are you sure your subquery is returning rows? Because SUM of an empty set is null not zero, eg
SQL> select to_char(counter)
2 from ( select level counter from dual connect by level <= 1000000 )
3 where counter =
4 (select sum(deptno) from scott.emp);
TO_CHAR(COUNTER)
----------------------------------------
310
SQL> select to_char(counter)
2 from ( select level counter from dual connect by level <= 1000000 )
3 where counter =
4 (select sum(deptno) from scott.emp where 1=0);
no rows selected
Your query says: Create all integers from 1 to 1,000,000 and of these show me the one that matches exactly the total amount of the table. So if that total is 123.45, you will get no row. If that amount is -123, you will get no row. If that amount is 1,000,001 you will get no row.
If you simply want the total, but you can have aggregations only in subqueries for some weird reason, just do
select * from (select sum(amount) as total from table) t;

Why does this sql snippet return 8 or 1 always?

What is the result of:
WITH Tbl AS (SELECT 5 AS A UNION SELECT 6 AS A)
SELECT COUNT(*) AS Tbl FROM Tbl AS A, Tbl AS B, Tbl AS C;
I know the result is supposed to be 8 but I don't know why. Also when I change both values (the 5 or 6) to the same thing it returns a table with the value 1 instead of 8 but all other instances it returns 8 no matter what numbers if they are different. I tested it out with an online sql executor.
Here is what the query does:
the common table expression (the subquery within the with clause) generates a derived table made of two rows
then, in the outer query, the from clause generates a cartesian product of this resultset twice: that's a total of 8 rows (2 * 2 * 2)
the select clause counts the number of rows - that's 8
The content of the rows in the with clause does not matter: this 5 and 6 could very well be foo and bar, or null and null, the result would be the same.
What makes a difference is the number of rows that the with clause generates. If it was generating just one row, you would get 1 as a result (1 * 1 * 1). If it was generating 3 rows, you would get 27 - and so on.
This expression:
WITH Tbl AS (SELECT 5 AS A UNION SELECT 6 AS A)
creates a (derived) table with two rows.
This expression:
WITH Tbl AS (SELECT 5 AS A UNION SELECT 5 AS A)
creates a (derived) table with one row, because UNION removes duplicates.
The rest of the query just counts the number of rows in the 3-way Cartesian product, which is either 111 or 222.

How to return the rows between 20th and 30th in Oracle Sql [duplicate]

This question already has answers here:
Oracle SQL: Filtering by ROWNUM not returning results when it should
(2 answers)
Closed 4 years ago.
so I have a large table that I'd like to output, however, I only want to see the rows between 20 and 30.
I tried
select col1, col2
from table
where rownum<= 30 and rownum>= 20;
but sql gave an error
I also tried --where rownum between 20 and 30
it also did not work.
so whats the best way to do this?
SELECT *
FROM T
ORDER BY I
OFFSET 20 ROWS --skips 20 rows
FETCH NEXT 10 ROWS ONLY --takes 10 rows
This shows only rows 21 to 30. Take care here that you need to sort the data, otherwise you may get different results every time.
See also here in the documentation.
Addendum: As in the possible duplicate link shown, your problem here is that there can't be a row with number 20 if there is no row with number 19. That's why the rownum-approach works to take only the first x records, but when you need to skip records you need a workaround by selecting the rownum in a subquery or using offset ... fetch
Example for a approach with using rownum (for lower oracle versions or whatever):
with testtab as (
select 'a' as "COL1" from dual
union all select 'b' from dual
union all select 'c' from dual
union all select 'd' from dual
union all select 'e' from dual
)
select * from
(select rownum as "ROWNR", testtab.* from testtab) tabWithRownum
where tabWithRownum.ROWNR > 2 and tabWithRownum.ROWNR < 4;
--returns only rownr 3, col1 'c'
Whenever you use rownum, it counts the rows that your query returns. SO if you are trying to filter by selecting all records between rownum 20 and 30, that is only 10 rows, so 20 and 30 dont exist. You can however, use WITH (whatever you want to name it) as and then wrap your query and rename your rownum column. This way you are selecting from your select. Example.
with T as (
select requestor, request_id, program, rownum as "ROW_NUM"
from fnd_conc_req_summary_v where recalc_parameters='N')
select * from T where row_num between 20 and 30;

SQL return multiple rows from one record

This is the opposite of reducing repeating records.
SQL query to create physical inventory checklists
If widget-xyz has a qty of 1 item return 1 row, but if it has 5, return 5 rows etc.
For all widgets in a particular warehouse.
Previously this was handled with a macro working through a range in excel, checking the qty column. Is there a way to make a single query instead?
The tables are FoxPro dbf files generated by an application and I am outputting this into html
Instead of generating an xml string and using xml parsing functions to generate a counter as Nestor has suggested, you might consider joining on a recursive CTE as a counter, as LukLed has hinted to:
WITH Counter AS
(
SELECT 0 i
UNION ALL
SELECT i + 1
FROM Counter
WHERE i < 100
),
Data AS
(
SELECT 'A' sku, 1 qty
UNION
SELECT 'B', 2
UNION
SELECT 'C', 3
)
SELECT *
FROM Data
INNER JOIN Counter ON i < qty
According to query analyzer, this query is much faster than the xml pseudo-table. This approach also gives you a recordset with a natural key (sku, i).
There is a default recursion limit of 100 in MSSQL that will restrict your counter. If you have quantities > 100, you can either increase this limit, use nested counters, or create a physical table for counting.
For SQL 2005/2008, take a look at
CROSS APPLY
What I would do is CROSS APPLY each row with a sub table with as many rows as qty has. A secondary question is how to create that sub table (I'd suggest to create an xml string and then parse it with the xml operators)
I hope this gives you a starting pointer....
Starting with
declare #table table (sku int, qty int);
insert into #table values (1, 5), (2,4), (3,2);
select * from #table;
sku qty
----------- -----------
1 5
2 4
3 2
You can generate:
with MainT as (
select *, convert(xml,'<table>'+REPLICATE('<r></r>',qty)+'</table>') as pseudo_table
from #table
)
select p.sku, p.qty
from MainT p
CROSS APPLY
(
select p.sku from p.pseudo_table.nodes('/table/r') T(row)
) crossT
sku qty
----------- -----------
1 5
1 5
1 5
1 5
1 5
2 4
2 4
2 4
2 4
3 2
3 2
Is that what you want?
Seriously dude... next time put more effort writing your question. It's impossible to know exactly what you are looking for.
You can use table with number from 1 to max(quantity) and join your table by quantity <= number. You can do it in many ways, but it depends on sql engine.
You can do this using dynamic sql.

generate_series() equivalent in DB2

I'm trying to search the DB2 equivalent of generate_series() (the PostgreSQL-way of generating rows). I obviously don't want to hard-code the rows with a VALUES statement.
select * from generate_series(2,4);
generate_series
-----------------
2
3
4
(3 rows)
The where clause needs to be a bit more explicit about the bounds of the recursion in order for DB2 to suppress the warning. Here's a slightly adjusted version that does not trigger the warning:
with dummy(id) as (
select 2 from SYSIBM.SYSDUMMY1
union all
select id + 1 from dummy where id < 4
)
select id from dummy
I managed to write a recursive query that fits :
with dummy(id) as (
select 2 from SYSIBM.SYSDUMMY1
union all
select id + 1 from dummy where id + 1 between 2 and 4
)
select id from dummy
The query can be adapted to whatever for(;;) you can dream of.