MS Access: Compare 2 tables with duplicates - sql

I have two tables which look like this:
T1:
ID | Date | Hour
T2:
ID | Date | Hour
I basically need to join these tables when their IDs, dates, and hours match. However, I only want to return the results from table 1 that do not match up with the results in table 2.
I know this seems simple, but where I'm stuck is the fact that there are multiple rows in table 1 that match up with table 2 (there are multiple intervals for any given hour). I need to return all of these intervals so long as they do not fall within the same hour period in table 2.
Example data:
T1:
1 | 1/1/2011 | 1
1 | 1/1/2011 | 1
1 | 1/1/2011 | 1
1 | 1/1/2011 | 2
T2:
1 | 1/1/2011 | 1
1 | 1/1/2011 | 1
My expected result set for this would be the last 2 rows from T1. Can anyone point me on the right track?.

I think you just want not exists:
select t1.*
from t1
where not exists (select 1
from t2
where t2.id = t1.id and t2.date = t1.date and t2.hour = t1.hour
);
EDIT:
I misread the question. This is very hard to do in MS Access. But, you can come close. The following returns the distinct rows in table 1 that do not have equivalent numbers in table 2:
select t1.id, t1.date, t1.hour, (t1.cnt - t2.cnt)
from (select id, date, hour, count(*) as cnt
from t1
group by id, date, hour
) t1 left join
(select id, date, hour, count(*) as cnt
from t2
group by id, date, hour
) t2 left join
on t2.id = t1.id and t2.date = t1.date and t2.hour = t1.hour
where t2.cnt < t1.cnt;

Related

Find the nearest future or equal to date from a table of dates with sql stmt

I have two tables
Table 1
ID | T1_Date
---+-------------
1 | 09/08/2020
2 | 09/30/2020
Table 2
T2_Date | Label
-----------+-----
08/31/2020 | Aug-20
09/20/2020 | Sep-20
10/25/2020 | Oct-20
I'm trying to have the result link the nearest future date label from table 2 with each record in table 1. So my output would look like:
ID | T1_Date | Label
---+------------+--------
1 | 09/08/2020 | Sep-20
2 | 09/30/2020 | Oct-20
So far I can only return all the records that are greater than the T1_Date value, so it repeats all the labels.
Is there a way to just grab the nearest future date label or the equal to label?
One method is a correlated subquery:
select t1.*,
(select max(t2.label) keep (dense_rank first t2.date asc)
from t2
where t2.date > t1.date
)
from t1;
there are many ways, you can solve this problem
One way which is pretty simple but not optimal:
select
t1.*
,(select Label from table2 where t2_date=(select MIN(T2_Date)from table2
where T2_Date>=T1_Date))Label
from table1 t1
-----------------------------------------------------------
The second Way:
with TempTable as
(
select T1_Date,min(T2_Date)T2_Date
from table2
left join table1 on T2_Date>=T1_Date
group by T1_Date
)
select t1.T1_Date,t2.Label from TempTable temp
left join table1 t1 on t1.T1_Date=temp.T1_Date
left join table2 t2 on t2.T2_Date=temp.T2_Date

SQL query which checks, for each row having a date range in t1, whether there exists any recorded date in t2 within that date range?

If I have two tables like so:
Table 1
Start | END | More columns ...
------------------------------------------
2019-10-20 | 2019-10-21 |...
Table 2
Log ID | DATE
--------------
1 | 2019-10-20
2 | 2019-10-22
I've tried to use CASE WHEN, Boolean, Exists but I suspect my logic is wrong somewhere.
I want to return a results table which includes all of the columns from Table 1, with an additional column containing a Boolean value of whether a date within the range for that row exists in the second table.
So the result set should then look like:
Start | End | MoreCols | Available
----------------------------------------------
2019-10-20 | 2019-10-21 | … | True
Use exists:
select t1.*,
(case when exists (select 1
from table2 t2
where t2.date between t1.start and t1.end
)
then 'True' else 'False'
end) as available_flag
from table1 t1
If your database supports boolean types, then you can write this as:
select t1.*,
(exists (select 1
from table2 t2
where t2.date between t1.start and t1.end
)
) as available_flag
from table1 t1

MS SQL Where one column is x or y and both returned

I have a table as follows with dates in. The table has many more records but simplified for asking purposes:
Name | Date | Grade
Person 1 | 01-01-2001 | B
Person 1 | 31-01-2001 | A
Person 2 | 01-01-2001 | C
Person 3 | 31-01-2001 | A
I want to return both records for Person 1 but not either of the other two. AND returns nothing obviously and OR returns everything. I want to search on the date not the grade or the person.
So the result would be:
Name | Date | Grade
Person 1 | 01-01-2001 | B
Person 1 | 31-01-2001 | A
One simple way to handle this is to aggregate by person and then assert that the two dates of interest are both present:
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT Name
FROM yourTable
WHERE Date IN ('2001-01-01', '2001-01-31')
GROUP BY Name
HAVING COUNT(DISTINCT Date) = 2
) t2
ON t1.Name = t2.Name
You can uses EXISTS to return a row if there exists another row with that name, having the other A/B grade.
select t1.*
from tablename t1
where t1.Date in ('2001-01-01', '2001-01-31')
and exists (select 1 from tablename t2
where t2.Name = t1.Name
and t2.Date in ('2001-01-01', '2001-01-31')
and t2.Date <> t1.Date)

How do I group a collection of elements by whether or not they have consecutive values?

So given a table like the one below, I would like to grab rows where id has at least three consecutive years.
+---------+--------+
| id | year |
+------------------+
| 2 | 2003 |
| 2 | 2004 |
| 1 | 2005 |
| 2 | 2005 |
| 1 | 2007 |
| 1 | 2008 |
+---------+--------+
The result over here would be of course:
+---------+
| id |
+---------+
| 2 |
+---------+
Any input at all as to how I could go about structuring a query to do this would be great.
This one works and can be fast when you have at least an index on the id-field:
WITH t1 AS (
SELECT *
FROM (VALUES
(2,2003),
(2,2004),
(1,2005),
(2,2005),
(1,2007),
(1,2008)
) v(id, year)
)
SELECT DISTINCT t1.id
FROM t1 -- your tablename
JOIN t1 AS t2 ON t1.id = t2.id AND t1.year + 1 = t2.year
JOIN t1 AS t3 ON t1.id = t3.id AND t1.year + 2 = t3.year;
You can use JOIN approach (self-join):
SELECT t1.id
FROM tbl t1
JOIN tbl t2 ON t2.year = t1.year + 1
AND t1.id = t2.id
JOIN tbl t3 ON t3.year = t1.year + 2
AND t1.id = t3.id
SQLFiddle
Combination (id, year) is UNIQUE
Typically guaranteed with a PRIMARY KEY or UNIQUE constraint or a unique index.
This is a general solution for any minimum number of consecutive rows:
SELECT DISTINCT id
FROM (
SELECT id, year - row_number() OVER (PARTITION BY id ORDER BY year) AS grp
FROM tbl
) sub
GROUP BY id, grp
HAVING count(*) > 2; -- minimum: 3
This should be faster than self-joining repeatedly, because only a single scan on the base table is needed. Test performance with EXPLAIN ANALYZE.
Related answer with detailed explanation:
Select longest continuous sequence
Combination (id, year) is not UNIQUE
You can make it unique in a first step.
SELECT DISTINCT id
FROM (
SELECT id, year - row_number() OVER (PARTITION BY id ORDER BY year) AS grp
FROM tbl
GROUP BY id, year
) sub
GROUP BY id, grp
HAVING count(*) > 2; -- minimum: 3
SQL Fiddle.
Or you could use the window function dense_rank() instead of row_number() and then count(DISTINCT year), but I don't see a benefit in this approach.
Understanding the sequence of events in a SELECT query is the key:
Best way to get result count before LIMIT was applied

Getting all the current effective records from a ORACLE table

I have two tables in oracle database
Table 1 say table1 with fields (id, name)
Records e.g.
###############
id | name
1 | Chair
2 | Table
3 | Bed
###############
and Table 2 say table2 with fields (id, table1_id, date, price)
##############################
id |table1_id| date | price
1 | 1 | 2013-09-09 | 500
2 | 1 | 2013-08-09 | 300
3 | 2 | 2013-09-09 | 5100
4 | 2 | 2013-08-09 | 5000
5 | 3 | 2013-09-09 | 10500
################################
What I want to achieve is to retrieve all the latest price of items from table 2
Result of SQL should be like
##############################
id |table1_id| date | price
1 | 1 | 2013-09-09 | 500
3 | 2 | 2013-09-09 | 5100
5 | 3 | 2013-09-09 | 10500
################################
I am able to run in mysql by following query
SELECT t2.id, t1.id, t1.name, t2.date, t2.price
FROM table1 t1 JOIN table2 t2
ON (t1.id = t2.table1_id
AND t2.id = (
SELECT id
FROM table2
WHERE table1_id = t1.id
ORDER BY table2.date DESC
LIMIT 1
));
but it's not working in ORACLE, Here i Need a query which can run on both server with minor modification
You may try this (shoud work in both MySQL and Oracle):
select t2.id, t2.table1_id, t2.dat, t2.price
from table1 t1 join table2 t2 on (t1.id = t2.table1_id)
join (select table1_id, max(dat) max_date
from table2 group by table1_id) tmax
on (tmax.table1_id = t2.table1_id and tmax.max_date = t2.dat);
This query may return several rows for the same table1_id and date if there are several prices in table2, like this:
##############################
id |table1_id| date | price
1 | 1 | 2013-09-09 | 500
2 | 1 | 2013-09-09 | 300
It's possible to change the query to retrieve only 1 row for each table1_id, but there should be some additional requirements (which row to choose in the above example)
if it doesn't matter then you may try this:
select max(t2.id) as id, t2.table1_id, t2.dat, max(t2.price) as price
from table1 t1 join table2 t2 on (t1.id = t2.table1_id)
join (select table1_id, max(dat) max_date
from table2 group by table1_id) tmax
on (tmax.table1_id = t2.table1_id and tmax.max_date = t2.dat)
group by t2.table1_id, t2.dat;
You can try this using GROUP BY instead, since you're not retrieving the product name from table1 except the product id (which is already in table2)
SELECT id,table1_id,max(date),price
FROM table2
GROUP BY id,table1_id,price
this is what you want :
select t2.id,t2.table1_id,t1.name,t2.pricedate,t2.price
from table1 t1
join
(
select id,table1_id, pricedate,price, row_number() over (partition by table1_id order by pricedate desc) rn
from table2
) t2
on t1.id = t2.table1_id
where t2.rn = 1