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

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.

Related

I'd like to return a single row in a query that joins two tables through a one to many relationship

This is an oracle system. I have a client table (one) and an account table (many). Frankly, I really just want to query the client table but due to poor design, there is a data element that I need for the client table that is only present on the account table - it will be the same value for all accounts for that client so I really just need to return one account row but I'm having problems accomplishing this. Here is the query I tried:
Select
c.client_num,
c.client_name,
a.agency_value
from client c
inner join account a on c.client_num = a.client_num
where a.account_num in (select a2.account_num from account a2 where rownum = 1)
You need to explicitly designate how to pick one record out of many. Here's one method - use MAX
Select
c.client_num,
c.client_name,
(SELECT
MAX(a.agency_value) agency_value
FROM account a
where c.client_num = a.client_num
-- not sure if this line is required - if not please remove
and a.account_num in (select a2.account_num from account a2 where rownum = 1)
) agency_value
from client c
Keep in mind that by implementing this you are "cementing" your bad table design.
Are you absolutely certain that there is only ever one agency_value? use query to find any clients that have more than one agency:
SELECT
a.client_num,
COUNT(DISTINCT a.agency_value) CountOfAgencyValues,
MAX(a.agency_value) max_agency_value,
MIN(a.agency_value) max_agency_value
FROM account a
GROUP BY a.client_num
HAVING COUNT(DISTINCT a.agency_value) > 1
With your input and doing some playing around on my own, here is the code that ultimately addressed my need:
select
c. client_num,
(select a.agency_value
from account a
where a.client_num = c.client_num
and rownum = 1)
from client c

Oracle select rows from a query which are not exist in another query

Let me explain the question.
I have two tables, which have 3 columns with same data tpyes. The 3 columns create a key/ID if you like, but the name of the columns are different in the tables.
Now I am creating queries with these 3 columns for both tables. I've managed to independently get these results
For example:
SELECT ID, FirstColumn, sum(SecondColumn)
FROM (SELECT ABC||DEF||GHI AS ID, FirstTable.*
FROM FirstTable
WHERE ThirdColumn = *1st condition*)
GROUP BY ID, FirstColumn
;
SELECT ID, SomeColumn, sum(AnotherColumn)
FROM (SELECT JKM||OPQ||RST AS ID, SecondTable.*
FROM SecondTable
WHERE AlsoSomeColumn = *2nd condition*)
GROUP BY ID, SomeColumn
;
So I make a very similar queries for two different tables. I know the results have a certain number of same rows with the ID attribute, the one I've just created in the queries. I need to check which rows in the result are not in the other query's result and vice versa.
Do I have to make temporary tables or views from the queries? Maybe join the two tables in a specific way and only run one query on them?
As a beginner I don't have any experience how to use results as an input for the next query. I'm interested what is the cleanest, most elegant way to do this.
No, you most probably don't need any "temporary" tables. WITH factoring clause would help.
Here's an example:
with
first_query as
(select id, first_column, ...
from (select ABC||DEF||GHI as id, ...)
),
second_query as
(select id, some_column, ...
from (select JKM||OPQ||RST as id, ...)
)
select id from first_query
minus
select id from second_query;
For another result you'd just switch the tables, e.g.
with ... <the same as above>
select id from second_query
minus
select id from first_query

SQL Query Performance with count

I have 2 tables, COMPANY and EMPLOYEE.
COMPANY_ID is the primary key of the COMPANY table and foreign key for EMPLOYEE table. The COMPANY_ID is a 10 digit number. We are generate a 3 number combination and query the database.
The select statement has regex to bulk load the company based on COMPANY_ID. The query is executed multiple times with different patterns
i.e.
regexp_like(COMPANY_ID, '^(000|001|002|003|004|005|006|007|008|009)') .
Existing query looks something like this -
select *
from COMPANY company
where regexp_like(company.COMPANY_ID, '^(000|001|002|003|004|005|006|007|008|009)')
The new requirement is to retrieve the company information along with the employee count. For example if a company has 10 employees, then the query should return all the columns of the COMPANY table, along with employee count i.e. 10
This is the select statement that I came up with -
select
nvl(count_table.cont_count, 0), company.*
from
COMPANY company,
(select company.COMPANY_ID, count(company.COMPANY_ID) as cont_count
from COMPANY company, EMPLOYEE employee
where regexp_like(company.COMPANY_ID, '^(000|001|002|003|004|005|006|007|008|009)')
and company.CONTACT_ID = employee.CONTACT_ID
group by (company.COMPANY_ID)) count_table
where
regexp_like(company.COMPANY_ID, '^(000|001|002|003|004|005|006|007|008|009)')
and count_table.COMPANY_ID(+)= company.COMPANY_ID
Above query works, but it takes double the time compared to the previous statement. Is there a better way to retrieve the employee count?
Note: Oracle database is in use.
You don't need to execute that expensive REGEXP_LIKE twice:
select nvl(count_table.cont_count,0),company.*
from COMPANY company
,( select employee.COMPANY_ID, count(employee.COMPANY_ID) as cont_count
from EMPLOYEE employee
group by (employee.COMPANY_ID)
) count_table
where regexp_like(company.COMPANY_ID, '^(000|001|002|003|004|005|006|007|008|009)')
and count_table.COMPANY_ID(+)= company.COMPANY_ID
Or you could use a scalar subquery:
select company.*
, (select count(*)
from employee e
where e.company_id = c.company_id
)
from COMPANY c
where regexp_like(c.COMPANY_ID, '^(000|001|002|003|004|005|006|007|008|009)')
And personally I would ditch the slow REGEXP_LIKE for something like:
where substr(c.company_id,1,3) between '000' and '009'
The derived table does not add value, thus I would get rid of it and use a scalar query (because I do not know all of your columns in the company table to properly do a group by):
select c.*,
nvl(
(select count(1)
from employee emp
where emp.company_id = c.company_id
),0) employee_count
from company c
where regexp_like(c.company_id, '^(000|001|002|003|004|005|006|007|008|009)')
Also, if performance is still an issue, I would consider modifying your where statement to not use a regexp.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Addendum
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I see that the question explicitly identifies that the employee table has company_id as a foreign key. Since this is clarified, I am removing this statement:
The data model for these tables is not intuitive (would you not have
company_id as a foreign key in the employees table?).

Oracle and Pagination

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).

select query with if in oracle

I need help! For example, there are four tables: cars, users, departments and join_user_department. Last table used for M: N relation between tables user and department because some users have limited access. I need to get the number of cars in departments where user have access. The table “cars” has a column department_id. If the table join_user_department doesn’t have any record by user_id this means that he have access to all departments and select query must be without any condition. I need do something like this:
declare
DEP_NUM number;--count of departments where user have access
CARS_COUNT number;--count of cars
BEGIN
SELECT COUNT (*) into DEP_NUM from join_user_departments where user_id=?;
SELECT COUNT(*) into CARS_COUNT FROM cars where
IF(num!=0)—it meant that user access is limited
THEN department_id IN (select dep_id from join_user_departments where user_id=?);
A user either has access to all cars (I'm assuming all cars are tied to a department, and the user has access to all departments) or the user has limited access. You can use a UNION ALL to bring these two groups together, and group by user to do a final count. I've cross joined the users with unlimited access to the cars table to associate them with all cars:
(UPDATED to also count the departments)
select user_id,
count(distinct department_id) as dept_count,
count(distinct car_id) as car_count,
from (
select ud.user_id, ud.department_id, c.car_id
from user_departments ud
join cars c on c.department_id = ud.department_id
UNION ALL
select u.user_id, v.department_id, v.car_id
from user u
cross join (
select d.department_id, c.car_id
from department d
join cars c on c.department_id = d.department_id
) v
where not exists (
select 1 from user_departments ud
where ud.user_id = u.user_id
)
)
group by user_id
A UNION ALL is more efficient that a UNION; a UNION looks for records that fall into both groups and throws out duplicates. Since each user falls into one bucket or another, UNION ALL should do the trick (doing a distinct count in the outer query also rules out duplicates).
"If the table join_user_department doesn’t have any record by user_id
this means that he have access to all departments"
This seems like very bad practice. Essentially you are using the absence of records to simulate the presence of records. Very messy. What happens if there is a User who has no access to a Car from any Department? Perhaps the current business logic doesn't allow this, but you have a "data model" which won't allow to implement such a scenario without changing your application's logic.

Resources