SQL Not IN query is taking time with similar table - sql

i have 2 tables suppose table_1 & table_2
table_1 has 56 columns and 1.2 million records
my query is like
table_1 like
RollNumber | Subject | G | Part | Status
------------------------------------------------
1 | 1 | 1 | 1 | 1
1 | 1 | 1 | 2 | 1
1 | 2 | 1 | 1 | 1
1 | 2 | 1 | 2 | 5
1 | 3 | 1 | 1 | 0
1 | 3 | 1 | 2 | 1
2 | 1 | 2 | 1 | 1
2 | 1 | 2 | 2 | 1
2 | 2 | 2 | 1 | 1
2 | 2 | 2 | 2 | 1
2 | 3 | 2 | 1 | 1
2 | 3 | 2 | 2 | 1
3 | 1 | 2 | 1 | 1
3 | 1 | 2 | 2 | 1
3 | 2 | 2 | 1 | 1
3 | 2 | 2 | 2 | 1
3 | 3 | 2 | 1 | 0
3 | 3 | 2 | 2 | 1
i want all RollNumber (group by with 2nd and third column) from table_1 where any status is 0 but don't want students who also have status = 5(or other than 1)
i have tried this
select * from table_1 as t1
inner join table_2 as t2
on t1.column2 = t2.column2 and t1.column3 = t2.column3 and t1.column4 = t2.column4
where t1.column1 not in
(select column1 from table_1 where status = 5)
This is the inner most query of my qhole query
i have also tried EXCEPT clause
Both queries take too long to execute

Starting with SQL Server 2008 you can use count() over() to count how many total rows in a given group have a certain value.
In this case you'll want to count the number of status <> 1 per group and to only select rows that belong to a group with a count of 0.
select * from (
select * ,
count(case when status <> 1 then 1 end) over(partition by RollNumber, G) c
from table_1
) t where c = 0

You can use EXISTS in place of NOT IN. This will be faster as there will be a boolean comparison instead of string comparison.
select * from table_1 as t1
inner join table_2 as t2
on t1.column1 = t2.column1 and t1.column2 = t2.column2 and t1.column3 = t2.column3
where not EXISTS
(select 1 from table_1 where status = 5 and t1.column3 = table_1.column3)

Try to use NOT EXISTS instead of NOT IN
SELECT *
FROM table_1 AS t1
INNER JOIN table_2 AS t2
ON t1.column1 = t2.column1 AND t1.column2 = t2.column2 AND t1.column3 = t2.column3
WHERE NOT EXISTS(
SELECT 1
FROM table_1
WHERE status=5 AND column3=t1.column3
)

Related

How to get count from one table which is mutually dependent to another table

I have two table
Let's name as first table: QC_Meeting_Master
Second table: QC_Project_Master I want to calculate count of problems_ID Which is mutually depend on second table
ID | QC_ID | Problems_ID |
___|_______|_____________|
1 | 1 | 2 |
2 | 1 | 7 |
ID | QC_ID | Problem_ID |
___|_______|_____________|
1 | 1 | 7 |
2 | 1 | 7 |
3 | 1 | 7 |
4 | 1 | 7 |
5 | 1 | 2 |
6 | 1 | 2 |
7 | 1 | 2 |
select COUNT(Problem_ID) from [QC_Project_Master] where Problem_ID in
(select Problems_ID from QC_Meeting_Master QMM join QC_Project_Master QPM on QMM.Problems_ID = QPM.Problem_ID)
I have to calculate Count of QC_Project_Master (problem_ID) on basis of QC_Meeting_Master (Problems_ID)
it means for first table: QC_Meeting_Master(Problems_ID) = 2,
then count should be 3
And for Second table: QC_Project_Master (Problems_ID) = 7,
then count should be 4
use conditional aggregation
select sum(case when t2.Problem_ID=2 then 1 else 0 end),
sum(case when t2.Problem_ID=7 then 1 else 0 end) from
table1 t1 join table2 t2 on t1.QC_ID=t2.QC_ID and t1.Problems_ID=t2.Problems_ID
if you need all the group count then use below
select t2.QC_ID,t2.Problems_ID, count(*) from
table1 t1 join table2 t2
on t1.QC_ID=t2.QC_ID and t1.Problems_ID=t2.Problems_ID
group by t2.QC_ID,t2.Problems_ID
As far as I understood your problem this is simple aggregation and JOIN as below:
SELECT mm.QC_ID, mm.Problem_ID, pm.cnt
FROM QC_Meeting_Master mm
INNER JOIN
(
SELECT QC_ID, Problem_ID, COUNT(*) cnt
FROM QC_Project_Master
GROUP BY QC_ID, Problem_ID
) pm
ON pm.QC_ID = mm.QC_ID AND pm.Problem_ID = mm.Problem_ID;

Oracle SQL - How Can I Join Two Tables, One to Many?

I am looking to do a full outer join in order to get the following results. Mainly I am joining table 1 to table 2.
However in table 1, anything that has a 0 in Column A but has the same available value in Key 1 column (ABC100 Key 1 value in table 1), only use that record (record 1 in table 1), and ignore 0 record (record 2 in table 1)
When joined to table 2, specifically for ABC100, I am expecting to see output lines 1 and 2 in the expected table results.
Any help or ideas going about this?
Example:
Table 1
| Key 1 | Column A |
| ABC100 | 100 |
| ABC100 | 0 |
| ABC300 | 200 |
| ABC400 | 300 |
Table 2
| Key 2 | Column C |
| ABC100 | 100 |
| ABC200 | 50 |
| ABC300 | 200 |
Expected results:
| Key 1 | Column A | Key 2 | Column B | NVL(A,0) - NVL(B,0)
| ABC100 | 100 | ABC100 | 100 | 0
| ABC100 | NULL | NULL | NULL | NULL
| NULL | NULL | ABC200 | 50 | -50
| ABC300 | 200 | ABC300 | 200 | 0
| ABC400 | 300 | NULL | NULL | 300
Your result set suggests that you want something like this:
SELECT t1.key1,
(CASE WHEN t1.a <> 0 THEN t1.a END) as a,
(CASE WHEN t1.a <> 0 THEN t2.key2 END) as key2,
(CASE WHEN t1.a <> 0 THEN t2.c END) as ,
(CASE WHEN t1.a <> 0 THEN COALESCE(t1.A, 0) - COALESCE(t2.B, 0) END) as diff
FROM t1 FULL JOIN
t2
ON t1.Key1 = t2.Key2;
Your description suggests that you want:
SELECT t1.key1, t1.a, t2.key2, t2.c,
COALESCE(t1.A, 0) - COALESCE(t2.B, 0) as diff
FROM (SELECT t1.*
FROM
WHERE t1.A <> 0 OR
NOT EXISTS (SELECT 1 FROM t1 tt1 WHERE tt1.key1 = t1.key1 AND tt1.key1 <> 0)
) t1 FULL JOIN
t2
ON t1.Key1 = t2.Key2;
You could use:
SELECT t1.*, t2.*, NVL(t1.A,0) - NVL(t2.B,0)
FROM tab1 t1
FULL JOIN tab2 t2
ON t1.Key1=t2.Key2

Return rows that of both table that has value in month column

I have 2 tables and I want to combine them in a way that they give data that are base on month. like these :
table1 :
month | data1 | data2
--------|-------|------
6 | 1 | 1
5 | 1 | 1
4 | 1 | 2
2 | 1 | 2
1 | 1 | 3
table2 :
month | data3
--------|------
7 | 1
6 | 1
4 | 1
3 | 1
1 | 2
=> tableResult :
month | data1 | data2 | data3
--------|-------|-------|-------
7 | 0 | 0 | 1
6 | 1 | 1 | 1
5 | 1 | 1 | 0
4 | 1 | 2 | 1
3 | 0 | 0 | 1
2 | 1 | 2 | 0
1 | 1 | 3 | 2
Is it possible to obtain that tableresult in sql query ?
or the only option is to query each table then process it with php?
by the way table1 and table2 are both views if it matters
use join and union
select t1.month,data1,data1,t2.data3
from table1 t1 left join table2 t2 on t1.month=t2.month
union
select t2.month,t1.data1,t1.date2,t2.data3
from table1 t1 right join table2 t2 on t1.month=t2.month
You are looking for full join:
select coalesce(t1.month, t2.month) as month,
coalesce(t1.data1, 0) as data1,
coalesce(t1.data2, 0) as data2,
coalesce(t2.data3, 0) as data3
from t1 full join
t2
on t1.month = t2.month;

How to mass-check for duplicates in MS-Access and log changes?

I want to mass-compare a few hundred fields in MS Access between 2 tables with identical column structures. If there are any differences between the column values, replaces the row in table1 with the new row in table2. If table2 does no longer holds a row that exists in table1, that row should be dropped from table1. All changes to table1 should be logged in tableLOGS.
Take for example:
____table1___ _____table2____ __________tableLOGS__________
| pid | A | B | | pid | A | B | | id | pid | A | B | action |
| 1 | 0 | 0 | | 1 | 0 | 0 | | 1 | 1 | 0 | 0 | add |
| 2 | 0 | 0 | | 2 | 0 | 1 | | 2 | 2 | 0 | 0 | add |
| 3 | 0 | 0 |
After running the desired SQL query, the result should be:
____table1___ _____table2____ __________tableLOGS__________
| pid | A | B | | pid | A | B | | id | pid | A | B | action |
| 1 | 0 | 0 | | 1 | 0 | 0 | | 1 | 1 | 0 | 0 | add |
| 2 | 0 | 1 | | 2 | 0 | 1 | | 2 | 2 | 0 | 0 | add |
| 3 | 2 | 0 | 1 | edit |
| 4 | 3 | 0 | 0 | delete |
I expect this would have to be broken down into 2 separate queries?
Mass-compare rows and update changes Log the changes to tableLOGS This seems like a fairly common task so perhaps MS Access has an easy way of accomplishing this? Thanks for all the help! :)
P.S. I am also open to simply deleting rows from table1 that do not match table2, and INSERT INTO table1 from table2.
This is from memory I didn't actually run this so it may need some fixing. My apologies
Start with the logging for updates
INSERT INTO TableLogs
SELECT A, B
FROM (
SELECT A, B
FROM table2 t2
INNER JOIN table1 t1 ON t1.pid = t2.pid
AND (t1.A <> t2.a OR t1.B <> t2.B)
WHERE table1.A IS NOT NULL)
Then update table1 with the updated values
UPDATE table1
INNER JOIN(
SELECT *
FROM table2 t2
INNER JOIN table1 t1 ON t1.pid = t2.pid
AND (t1.A <> t2.a OR t1.B <> t2.B)
WHERE table1.A IS NOT NULL) t2
ON table1.pid = t2.pid
Log missing records in table 1
INSERT INTO tablelogs
SELECT A, B
FROM table2
INNER JOIN table1 t1 ON t1.pid = t2.pid AND (t1.A <> t2.a OR t1.B <> t2.B)
WHERE table1.A IS NOT NULL
Delete the missing rows from table1
DELETE table1
WHERE pid NOT IN
(SELECT pid FROM Table2)
You could also put a trigger on table1 to update table logs but its not really best practice.
Hope that helps, like I said I didn't run it yet.

Select Query Joined on Two Fields?

I've got a few tables in an access database:
ID | LocationName
1 | Location1
2 | Location2
ID | LocationID | Date | NumProductsDelivered
1 | 1 | 12/10 | 3
2 | 1 | 01/11 | 2
3 | 1 | 02/11 | 2
4 | 2 | 11/10 | 1
5 | 2 | 12/10 | 1
ID | LocationID | Date | NumEmployees | EmployeeType
1 | 1 | 12/10 | 10 | 1 (=Permanent)
2 | 1 | 12/10 | 3 | 2 (=Temporary)
3 | 1 | 12/10 | 1 | 3 (=Support)
4 | 2 | 10/10 | 1 | 1
5 | 2 | 11/10 | 2 | 1
6 | 2 | 11/10 | 1 | 2
7 | 2 | 11/10 | 1 | 3
8 | 2 | 12/10 | 2 | 1
9 | 2 | 12/10 | 1 | 3
What I want to do is pass in the LocationID as a parameter and get back something like the following table. So, if I pass in 2 as my LocationID, I should get:
Date | NumProductsDelivered | NumPermanentEmployees | NumSupportEmployees
10/10 | | 1 |
11/10 | 1 | 2 | 1
12/10 | 1 | 2 | 1
It seems like this should be a pretty simple query. I really don't even need the first table except as a way to fill in the combo box on the form from which the user chooses which location they want a report for. Unfortunately, everything I've done has resulted in me getting a lot more data than I should be getting. My confusion is in how to set up the join (presumably that's what I'm looking for here) given that I want both the date and locationID to be the same for each row in the result set.
Any help would be much appreciated.
Thanks.
EDIT:
Ok - the answer below didn't quite work, but it did set me on the right track and I was able to use the following query:
SELECT t1.Date, t2.NumProductsDelivered,
(SELECT t1a.NumEmployees
FROM table3 t1a
WHERE t1a.EmployeeType=1 AND t1a.LocationID=t1.LocationID AND t1a.Date= t1.Date)
AS "PermEmps",
(SELECT t1b.NumEmployees
FROM table3 t1b
WHERE t1b.EmployeeType=3 AND t1b.LocationID=t1.LocationID AND t1b.Date=t1.Date)
AS "SupportEmps"
FROM table3 AS t1 LEFT JOIN table2 AS t2 ON (t2.Date=t1.Date)
AND (t2.LocationID=t1.LocationID)
WHERE t1.LocationID=2
GROUP BY t1.Date, t1.LocationID, t2.NumProductsDelivered;
This is getting me the results I was looking for. However, in a case where the location has a break between products being delivered, I don't see the correct results. It seems that the recordset stops as soon as there's an empty row and then never picks back up again. So, where I might expect to see this:
Date | NumProductsDelivered | NumPermanentEmployees | NumSupportEmployees
10/10 | | 1 |
11/10 | 1 | 2 | 1
12/10 | 1 | 2 | 1
01/10 | 2 | | 1
06/10 | 1 | |
I only see this:
Date | NumProductsDelivered | NumPermanentEmployees | NumSupportEmployees
10/10 | | 1 |
11/10 | 1 | 2 | 1
12/10 | 1 | 2 | 1
01/10 | 2 | | 1
I think this would work:
DECLARE #LocationId int
SET #LocationId=2
SELECT L2.LocationId, L2.Date, COUNT(DISTINCT NumProductsDelivered) as NumProductsDelivered,
SUM(case when L2.EmployeeType =1 then NumEmployees else 0 end) as NumPermanentEmployees,
SUM(case when L2.EmployeeType =3 then NumEmployees else 0 end) as NumSupportEmployees
FROM L1
RIGHT JOIN L2
ON L1.LocationID=L2.LocationID
AND L1.Date=L2.Date
WHERE L2.LocationId=#LocationId
GROUP BY L2.LocationId, L2.Date
Something like this should work:
[deleted original]
Try this instead (untested):
select t3.date, t2.numproductsdelivered,
(select sum(t3.numemployees)
from table3 t3a
where t3a.locationid = t3.locationid and t3a.date = t3.date and t3a.employeetype = 1
) as numpermanentemployees,
(select sum(t3.numemployees)
from table3 t3b
where t3b.locationid = t3.locationid and t3b.date = t3.date and t3b.employeetype = 3
) as numsupportemployees
from table3 as t3
left join table2 as t2 on t2.locationid = t3.locationid and t2.date = t3.date
where t3.locationid = 2
group by t3.date, t2.numproductsdelivered
If you didn't mind having separate rows for each employee type it could be simplified:
select t3.date, t2.numproductsdelivered, t3.employeetype, sum(t3.numemployees) as numemployees
from table3 as t3
left join table2 as t2 on t2.locationid = t3.locationid and t2.date = t3.date
where t3.locationid = 2 and t3.employeetype in (1, 3)
group by t3.date, t2.numproductsdelivered, t3.employeetype
Edit: Try this query:
SELECT t1.Date
FROM table3 AS t1
WHERE t1.LocationID=2
GROUP BY t1.Date
...and see if you get all the dates.
Then add the left join:
SELECT t1.Date, t2.NumProductsDelivered
FROM table3 AS t1 LEFT JOIN table2 AS t2 ON (t2.Date=t1.Date)
AND (t2.LocationID=t1.LocationID)
WHERE t1.LocationID=2
GROUP BY t1.Date, t1.LocationID, t2.NumProductsDelivered;
If it's doing a left INNER join, then it will remove rows from t1 that don't have a matching row in t2. Try explicitly setting a left OUTER join and see if that works. The RDBMS I've used the most defaults to outer, but maybe yours (Access) defaults to inner.
So I am thinking the following will work (add "OUTER" and remove "t1.LocationId"):
SELECT t1.Date, t2.NumProductsDelivered,
(SELECT t1a.NumEmployees
FROM table3 t1a
WHERE t1a.EmployeeType=1 AND t1a.LocationID=t1.LocationID AND t1a.Date= t1.Date)
AS "PermEmps",
(SELECT t1b.NumEmployees
FROM table3 t1b
WHERE t1b.EmployeeType=3 AND t1b.LocationID=t1.LocationID AND t1b.Date=t1.Date)
AS "SupportEmps"
FROM table3 AS t1 LEFT OUTER JOIN table2 AS t2 ON (t2.Date=t1.Date)
AND (t2.LocationID=t1.LocationID)
WHERE t1.LocationID=2
GROUP BY t1.Date, t2.NumProductsDelivered;