Oracle and Pagination - oracle

In MySql, the concept of pagination can easily be implemented with a single SQL statement using the LIMIT clause something like the following.
SELECT country_id, country_name
FROM country c
ORDER BY country_id DESC
LIMIT 4, 5;
It would retrieve the rows starting from 5 to 10 in the result set which the SQL query retrieves.
In Oracle, the same thing can be achieved using row numbers with a subquery making the task somewhat tedious as follows.
SELECT country_id, country_name
FROM
(SELECT rownum as row_num, country_id, country_name
FROM
(SELECT country_id, country_name
FROM country
ORDER BY country_id desc)
WHERE rownum <= 10
)
WHERE row_num >=5;
In Oracle 10g (or higher, I'm not sure about the higher versions though), this can be made somewhat easy such as,
SELECT country_id, country_name
FROM (SELECT country_id, country_name, row_number() over (order by country_id desc) rank
FROM country)
WHERE rank BETWEEN 6 AND 10;
Regarding an application like a web application, the concept of pagination is required to implement almost everywhere and writing such SQL statements every time a (select) query is executed is sometimes a tedious job.
Suppose, I have a web application using Java. If I use the Hibernate framework then there is a direct way to do so using some methods supported by Hibernate like,
List<Country>countryList=session.createQuery("from Country order by countryId desc")
.setFirstResult(4).setMaxResults(5).list();
but when I simply use JDBC connectivity with Oracle like,
String connectionURL = "jdbc:oracle:thin:#localhost:1521:xe";
Connection connection = null;
Statement statement = null;
ResultSet rs = null;
Class.forName("oracle.jdbc.OracleDriver").newInstance();
connection = DriverManager.getConnection(connectionURL, "root", "root");
statement = connection.createStatement();
rs = statement.executeQuery("SELECT * from country");
My question in this case, is there a precise way to retrieve a specified range of rows using this code? Like in the preceding case using the methods something like setFirstResult() and setMaxResults()? or the only way to achieve this is by using those subqueries as specified.

Because 'No' is an answer too:
Unfortunately, you will have to use the subquery approach. I would personally use the one with the rank (the second one).

Related

how to find clients that meet the same criteria in one column

I have a column that is like this:
Client ID
Service Name
123456
housing
122458
transportation
125837
education
125837
transportation
173947
Childcare
I am trying to extract data from an Oracle database for all clients who have accessed education and transportation services only. Could anyone please help me write my query?
I have created a Where statement in my query that goes like this:
where ch.service_name IN ('education', 'transportation)
however, this query gives me all clients who have accessed education and or transportation when in fact, I only want data for clients who have accessed both education and or transportation.
thank you
Seems you need a HAVING Clause along with GROUP BY after restricting to only those two service_name such as
SELECT ClientID
FROM t
WHERE service_name IN ('education', 'transportation')
GROUP BY ClientID
HAVING COUNT(DISTINCT service_name) = 2
EDIT(depending on the comment) : One option would be converting the query into the one which contains a COUNT() analytic function such as
WITH t2 AS
(
SELECT t.*, COUNT(DISTINCT service_name) OVER (PARTITION BY ClientID) AS cnt
FROM t
WHERE service_name IN ('education', 'transportation')
)
SELECT *
FROM <the_other_table>
LEFT JOIN t2
WHERE cnt = 2
in order to combine with your current query as desired.

Oracle Table Variables

Using Oracle PL/SQL is there a simple equivalent of the following set of T-SQL statements? It seems that everything I am finding is either hopelessly outdated or populates a table data type with no explanation on how to use the result other than writing values to stdout.
declare #tempSites table (siteid int)
insert into #tempSites select siteid from site where state = 'TX'
if 10 > (select COUNT(*) from #tempSites)
begin
insert into #tempSites select siteid from site where state = 'OK'
end
select * from #tempSites ts inner join site on site.siteId = ts.siteId
As #AlexPoole points out in his comment, this is a fairly contrived example.
What I am attempting to do is get all sites that meet a certain set of criteria, and if there are not enough matches, then I am looking to use a different set of criteria.
Oracle doesn't have local temporary tables, and global temporary tables don't look appropriate here.
You could use a common table expression (subquery factoring):
with tempSites (siteId) as (
select siteid
from site
where state = 'TX'
union all
select siteid
from site
where state = 'OK'
and (select count(*) from site where state = 'TX') < 10
)
select s.*
from tempSites ts
join site s on s.siteid = ts.siteid;
That isn't quite the same thing, but gets all the TX IDs, and only includes the OK ones if the count of TX ones - which has to be repeated - is less than 10. The CTE is then joined back to the original table, which all seems a bit wasteful; you're hitting the same table three times.
You could use a subquery directly in a filter instead:
select *
from site
where state = 'TX'
or (state = 'OK'
and (select count(*) from site where state = 'TX') < 10);
but again the TX sites have to be retrieved (or at least counted) a second time.
You can do this with a single hit of the table using an inline view (or CTE if you prefer) with an analytic count - which add the count of TX rows to the columns in the actual table, so you'd probably want to exclude that dummy column from the final result set (but using * is bad practice anyway):
select * -- but list columns, excluding tx_count
from (
select s.*,
count(case when state = 'TX' then state end) over (partition by null) as tx_count
from site s
where s.state in ('TX', 'OK')
)
where state = 'TX'
or (state = 'OK' and tx_count < 10);
From your description of your research it sounds like what you've been looking at involved PL/SQL code populating a collection, which you could still do, but it's probably overkill unless your real situation is much more complicated.

Optimize multiple subselects with WITH clause in Oracle

I have a query like:
select
qsn.code,
(select prs.display_name from prs where prs.id = qsn.fk_prs) display_name,
(select prs.address from prs where prs.id = qsn.fk_prs) address,
(select prs.tel from prs where prs.id = qsn.fk_prs) tel
from
qsn
where
qsn.register_date between :x1 and :x2
When I look at the execution plan of the query, it queries prs table 3 times (each time using INDEX UNIQUE SCAN).
I wonder if I can query the prs table once using WITH clause? How can I write the query that way.
I shall mention that because each of the tables have millions of record, joining them makes the query so slow.
using with clause your query goes like this:
with abc as (select id,
display_name ,
address ,
tel
from prs)
select
qsn.code,
abc.display_name,
abc.address,
abc.tel
from qsn
inner join abc
on qsn.fk_prs = abc.id
where qsn.register_date between :x1 and :x2 ;
ps: not tested.
Use a join:
select qsn.code, prs.display_name, prs.address, prs.tel
from qsn
left join prs on prs.id = qsn.fk_prs
where qsn.register_date between :x1 and :x2

cascading Input Control sql query return error: "ORA-01427: single-row subquery returns more than one row"

looking for solution on my sql query error.I'm trying to create second cascading Input Control in JaspersoftServer. The first Input Control works fine, however when I try to create a second cascade IC it returns with the error. I have 3 tables (user, client, user_client), many to many, so 1 linked table (user_client) between them.The 1st Input Control (client) - works well, end user will select the client, the client can have many users, so cascade is the key. Also, as the output, I would like to get not the user_id, but user's firstname and the lastname as one column field. And here is where i'm stuck. I'm pretty sure it is simple syntaxis error, but spent a good couple of hours to figure out what is wrong with it. Is anyone can have a look at it please and indicate where is the problem in my query ?! So far I've done:
select distinct
u.user_id,(
SELECT CONCAT(first_name, surname) AS user_name from tbl_user ),
c.client_id
FROM tbl_user u
left join tbl_user_client uc
on uc.user_id = u.user_id
left join tbl_client c
on c.client_id = uc.client_id
where c.client_id = uc.client_id
order by c.client_id
Thank you in advance.
P.S. JasperServer + Oracle 11g
You're doing an uncorrelated subquery to get the first/last name from the user table. There is no relationship between that subquery:
SELECT CONCAT(first_name, surname) AS user_name from tbl_user
... and the user ID in the main query, so the subquery will attempt to return every first/last name for all users, for every row your joins find.
You don't need to do a subquery at all as you already have the tbl_user information available:
select u.user_id,
CONCAT(u.first_name, u.surname) AS user_name
c.client_id
FROM tbl_user u
left join tbl_user_client uc
on uc.user_id = u.user_id
left join tbl_client c
on c.client_id = uc.client_id
where c.client_id = uc.client_id
order by c.client_id
If you want to put a space between the first and last name you'll either need nested concat() calls, since that function only takes two arguments:
select u.user_id,
CONCAT(u.first_name, CONCAT(' ', u.surname)) AS user_name
...
... or perhaps more readably use the concatenation operator instead:
select u.user_id,
u.first_name ||' '|| u.surname AS user_name
...
If the first control has selected a client and this query is supposed to find the users related to that client, you're joining the tables the wrong way round, aren't you? And you aren't filtering on the selected client - but no idea how that's actually implemented in Jasper. Maybe you do want the entire list and will filter it on the Jasper side.

Oracle: Order by Union returning ORA-00933: SQL command not properly ended

I have an issue with using Oracle's union and order by clauses together.
I have two complex queries (with sub queries in them) having an order by clause for each of them. I need to union the output of both and return the result. When I run it, I am getting the error ORA-00933: SQL command not properly ended.
But it works when I comment out the order by clauses in both of them.
To test this, I created a simple query as simple as shown below
select * from employee where employee_id=2 order by name
union
select * from employee where employee_id=3 order by name;
Even this gave the same error when ran with order by clauses but runs well when I commentout the order by clauses.
I tried searching forums, but I could not get solution for the exact problem. I found one at ORACLE Query with ORDER BY and UNION but As my queries are already too complecated because of subqueries and joins between too many tables, I dont want to implement this.
Can someone help me on fixing the root cause of the issue.
try this code:
select e1.name name /* e1.* */
from employee e1
where employee_id = 2
union
select
e2.name name /* e2.* */
from employee e2
where employee_id = 3
order by name;
if you want to order the result of first query then to order the result the second query so you can do like this:
select 1 query, e1.name name /* e1.* */
from employee e1
where employee_id = 2
union
select
2 query, e2.name name /* e2.* */
from employee e2
where employee_id = 3
order by query, name;
You can have only one ORDER BY when combining multiple queries, on the last statement. The ORDER BY clause acts on the entire set.
See the Oracle Documentation:
You cannot specify the order_by_clause in the subquery of these operators.
If you want order by in each query you must wrap it in other select as a subquery:
select * from (select * from employee where employee_id=2 order by name)
union
select * from (select * from employee where employee_id=3 order by name);

Resources