"NOT IN" SQL across 2 oracle databases - sql

I have 2 Oracle databases A & B. I connect to A and run following SQLs:
SELECT * FROM employee has 2000 rows.
SELECT empId FROM B.xx has 700 rows.
SELECT * FROM employee ​WHERE empId IN (SELECT empId FROM B.xx) has 698 rows.
SELECT * FROM employee WHERE empId NOT IN (SELECT empId FROM B.xx) return nothing!!!
I don't know why the last SQL didn't return anything. Is it because of "cross database"?
I can't re-produce this if I replace B.xx to another table in A.

The most probable reason is that empId has NULL values in the table B.xx.
Try to run
SELECT * FROM employee WHERE empId NOT IN (SELECT empId FROM B.xx where empId is not NULL)
this will give you a valid count.
That's just how not in works.

NOT IN does not behave as expected if any of the results returned by the subquery are NULL. As you observe, it returns no rows at all.
For this reason, I strongly recommend always using NOT EXISTS:
SELECT e.*
FROM employee e
WHERE NOT EXISTS (SELECT 1 FROM B.xx x WHERE x.empId = e.empId);
Although you can fix the problem by adding a WHERE x.empId IS NOT NULL, I think that is a dangerous habit. Sometime you are likely to leave out the WHERE clause and mistakenly think that there are no mismatches (yes, this has happened to me). This cannot happen if you always use NOT EXISTS.

Related

PostgreSQL, NOT IN clause

I want to calculate DAU and exclude user that we don't consider "real" (employees, beta testers etc).
It worked fine previously when I wrote the filtering in the query:
SELECT
count(distinct user_id) AS daily,
e.event_timestamp::DATE AS date
FROM
"public"."events" AS e
WHERE
user_id IN (SELECT
distinct id
from
"user"."user"
WHERE
username IS NOT NULL AND position IS NOT NULL )
GROUP BY date
When I try changing it to below, which should give more or less the same count (basically instead of defining the 4000 "real users" I define the 1000 "non-users" I want to exclude). However, this gives me way higher counts. It's like the distinct statement isn't working.
I added the NOT NULL to the subquery but doesn't change the result. Is there something with the NOT IN + subquery that works in another way than the IN clause?
SELECT
count(distinct e.user_id) AS daily,
e.event_timestamp::DATE AS date
FROM
"public"."events" AS e
WHERE
e.user_id NOT IN (SELECT distinct id FROM "public"."non_users" WHERE id IS NOT NULL)
GROUP BY
date
ORDER BY
date
Yes. If any of the values in the subquery are NULL, then NOT IN returns no rows For this reason, I strongly recommend that you always use NOT EXISTS -- it behaves as expected.
You seem to know this, because you are using a NULL comparison in the WHERE. So, the difference is probably due to the other condition. So, include it as well:
SELECT count(distinct e.user_id) AS daily,
e.event_timestamp::DATE AS date
FROM "public"."events" e
WHERE NOT EXISTS (SELECT 1
FROM "public"."non_users" nu
WHERE e.user_id = nu.id AND
nu.position IS NOT NULL
)
GROUP BY date
ORDER BY date;

Selecting multiple ID's from a table gives error ORA-02070

When I try to select multiple ID's from a table, I am getting error ORA-02070.
Here is the query that I am using:
select *
from hrs_employee_store
where employee_id in (13511677, 576000);
Here is the error which I am getting:
ORA-02070: database ODS_XSTORE does not support TO_NUMBER in this context
Also, when I use this query,
select * from hrs_employee_store
where employee_id in ('13511677', '576000');
I am just getting the row for 13511677.
Is there a way to fix this issue? Thanks
I suspect that EMPLOYEE_ID is not a number. Try:
select *
from hrs_employee_store
where EMPLOYEE_ID in ('13511677', '576000');
This returns matching employees, meaning there is no match for the second.
If you want NULL values for all the extra columns, you can use left join:
select *
from (select '13511677' as employee_id from dual union all
select '576000'
) eid left join
hrs_employee_store es
using (employee_id);

SQL Server select duplicated rows

I am newbie to SQL Server, and I want to select all those who changed their department at least once.
The table structure is:
BusinessEntityID
DepartmentID
ShiftID
StartDate
RateChangeDate
Rate
NationalIDNumber
I have the following code to generate an intermediate table
select distinct
DepartmentID, NationalIDNumber
from
Table
where
NationalIDNumber in (select NationalIDNumber
from Ben_VEmployee
group by NationalIDNumber
having count(NationalIDNumber) > 1)
Output:
DepartmentID NationalIDNumber
-----------------------------
1 112457891
2 112457891
4 24756624
4 895209680
5 24756624
5 895209680
7 259388196
My questions is: how to remove non-duplicate records in the intermediate table as above?
So record "7 - 259388196" should be removed because he did not change department.
Thanks.
Try using group by and comparing the maximum and minimum department. If it changed, then these will be different:
select NationalIDNumber
from Ben_VEmployee
group by NationalIDNumber
having min(DepartmentID) <> max(DepartmentID);
If you need the actual departments, you can join this back in to the original data.
If you want a list of every ID number that has been in more than one department, you can use
SELECT COUNT(DepartmentID) AS noDepartments
, NationalIDNumber
FROM Table
GROUP BY NationalIDNumber
HAVING COUNT(DepartmentID) > 1
If you want to delete the records for the deparment the employee used to be in, but isn't any more, than you'd have to know which department that was to know which record to delete! If you do know this, then say, and we can work it out.

How to get records from both tables using ms access query

I have 2 Tables in Ms Access
tbl_Master_Employess
tbl_Emp_Salary
I want to show all the employees in the employee table linked with employee salary table
to link both table the id is coluqEmpID in both table
In the second table, I have a date column. I need a query which should fetch records from both tables using a particular date
I tried the following query:
select coluqEID as EmployeeID , colEName as EmployeeName,"" as Type, "" as Amt
from tbl_Master_Employee
union Select b.coluqEID as EmployeeID, b.colEName as EmployeeName, colType as Type, colAmount as Amt
from tbl_Emp_Salary a, tbl_Master_Employee b
where a.coluqEID = b.coluqEID and a.colDate = #12/09/2013#
However, it shows duplicates.
Query4
EmployeeID EmployeeName Type Amt
1 LAKSHMANAN
1 LAKSHMANAN Advance 100
2 PONRAJ
2 PONRAJ Advance 200
3 VIJAYAN
4 THIRUPATHI
5 VIJAYAKUMAR
6 GOVINDAN
7 TAMILMANI
8 SELVAM
9 ANAMALAI
10 KUMARAN
How would I rewrite my query to avoid duplicates, or what would be a different way to not show duplicates?
The problem with your query is that you are using union when what you want is a join. The union is first going to list all employees with the first part:
select coluqEID as EmployeeID , colEName as EmployeeName,"" as Type, "" as Amt
from tbl_Master_Employee
and then adds to that list all employee records where they have a salary with a certain date.
Select b.coluqEID as EmployeeID, b.colEName as EmployeeName, colType as Type,
colAmount as Amt
from tbl_Emp_Salary a, tbl_Master_Employee b
where a.coluqEID = b.coluqEID and a.colDate = #12/09/2013#
Is your goal to get a list of all employees and only display salary information for those who have a certain date? Some sample data would be useful. Assuming the data here: SQL Fiddle this query should create what you want.
Select a.coluqEID as EmployeeID, colEName as EmployeeName,
b.colType as Type, b.colAmount as Amt
FROM tbl_Master_Employees as a
LEFT JOIN (select coluqEID, colType, colAmount FROM tbl_EMP_Salary
where colDate = '20130912') as b ON a.coluqEID = b.coluqEID;
The first step is to create a select that will get you just the salaries that you want by date. You can then perform a join on this as if you were performing a separate query. You use a LEFT JOIN because you want all of the records from one side, the employees, and only the records that match your criteria from the second side, your salaries.
I believe you will need a join, however as to your question on Unique names.
select **DISTINCT** coluqEID as EmployeeID
Adding the distinct operator would give only uniquely returned results.

How to query on another query result?

I have a problem as I am not so strong on queries.
I have a query with consists of a union of two select queries :
SELECT em.emp_code,
em.emp_name,
COALESCE(SUM(pe.hours_allotted),0) AS hours,
pe.dated
FROM employee_master em
LEFT JOIN project_employee pe ON (pe.Emp_code = em.emp_code)
WHERE (dated >= '2011-03-14'
AND dated < '2011-03-20' )
OR dated IS NULL
GROUP BY em.emp_code
UNION
(SELECT em.emp_code,
em.emp_name,
'0' AS hours,
pe.dated
FROM employee_master em
LEFT JOIN project_employee pe ON (pe.Emp_code = em.emp_code)
WHERE (dated >= '2011-03-14'
AND dated < '2011-03-20' )
OR dated IS NOT NULL
GROUP BY em.Emp_code)
ORDER BY emp_name;
Now the result sets are returning for example as:
ecode ename hours
----------------------
201 Alak basu 10
201 alak basu 0
The first result is from first select statement of the union where hours = 10
and hours = 0 is from second select statement of union.
What I want is:
ecode ename hours
----------------------------
201 alak basu 10
Like in the case there should be only one result per ecode. How to group it like summing up the hours on as group by ecode so that it gives me only one result as above?
You can always do something like:
select emp_code, min(emp_name) as emp_name, sum(hours)
from (
<your original query here>
) as e
group by emp_code
order by emp_name;
If the desired result is to sum all hours for a single employee code into a single row, and the second query after the UNION will only ever return zero hours, it seems like the best solution here is to get rid of the UNION.
EDIT: After further clarification, here's what I think the SQL should probably look like:
SELECT em.emp_code,
em.emp_name,
COALESCE(pe.hours, 0) AS hours
FROM employee_master em
LEFT JOIN (
SELECT emp_code,
SUM(hours_allotted) AS hours
FROM project_employee
WHERE dated >= '2011-03-14' AND
dated < '2011-03-20'
GROUP BY emp_code
) pe ON (pe.emp_code = em.emp_code)
ORDER BY em.emp_name;
What it's doing:
Perform a subquery to filter all project_employee entries to the ones within the specified date range. (Note that there is no need for NULL or NOT NULL checks at all here. Either the date is in range, or it is not.)
Sum the hours for each employee code generated in the subquery.
Take all employees in the employee_master table, and search for matching entries in the filtered, summed project_employee subquery result set. (Since it is a LEFT JOIN, every employee in the master table will have an entry, even if none of the filtered project_employee entries matched.)
In the case that there is no match, the pe.hours column will be NULL, causing the COALESCE to revert to its second value of zero.
Order results by emp_name.