Nested SELECT, or am I over-complicating? - mysql

I have the following tables:
table1
id
name
number
status
table2
id
table1_id
transaction_no
transaction_date
I want to return a list of table1.name, table1.num, table2.transaction_no where the table1.status = "Active" and the lastest table2.transaction_date is older than 4 months.
I keep trying something like this but the syntax fails in various places.
SELECT tab1.name, tab1.num, tab2.transaction_no
FROM table1 AS tab1
INNER JOIN table2 AS tab2 ON tab1.id = tab2.table1_id
WHERE tab1.status = "Active"
AND
(SELECT MAX(tab2.transaction_date) FROM tab2
GROUP BY tab1.name) <= (date 4 months ago)
I know this is not right but my brain is struggling to wrap around the idea, or I making it more complicated that it is. Any suggestions or guidance would be greatly appreciated.

That GROUP BY tab1.name doesn't make sense in your subquery. tab1 isn't in context in your subquery (nor istab2 for that matter). Instead though you could do a correlated subquery:
AND (SELECT MAX(t2.transaction_date) FROM table2 t2 WHERE t2.table1_id = tab1.id) <= (date 4 months ago)
Now that subquery can stand on it's own, and it is properly correlated to the main query by way of it's WHERE clause.
If you only want the latest t2 record (and it has to be more than 4 months ago) then a subquery in your FROM clause would work:
SELECT tab1.name, tab1.num, tab2.transaction_no
FROM table1 AS tab1
INNER JOIN (
SELECT transaction_no, table1_id
FROM table2 t2
WHERE transaction_date = (SELECT max(transaction_date) FROM table2 WHERE table1_id = t2.table1_id)
) AS tab2
ON tab1.id = tab2.table1_id
WHERE tab1.status = "Active";
Here we are still using a correlated subquery to grab the record from table2 that has the greatest transaction_date for each table1_id. Then we are joining that one record per table1_id back to table1.id.

Related

How to SELECT date and time within that date?

I read few articles about this: Select max date, then max time This one seems most helpful but I do not see way to implement it.
There is five tables. I join them. I need to select only one row with highest date and highest time from first table and same from second table and join the rest on some other value. With the code I wrote I get multiple rows. It seems time selection is not right.
It might be done with subquery in subquery. I've tried something like this:
SELECT * from table1
INNER JOIN table2 ON table1.date = table2.date AND table1.gm = table2.gm
INNER JOIN table3 ON table2.gm = table3.gm ...
WHERE table3.date = :date AND table4.date = :date ...
AND table1.date IN(
SELECT MAX(table1.date) FROM table1 WHERE table1.time IN(
SELECT MAX(table1.time) FROM table1
)
)
AND table2.date IN(
SELECT MAX(table2.date) FROM table1 WHERE table2.time IN(
SELECT MAX(table2.time) FROM table2 )
)
ORDER BY table1.id
Question is:
How to get single row after joining all of this where date is highest and time is highest on that date?
Thanks!
EDIT: I am sorry for this. I forgot to say that I need max time of max date related with specific value from tables(gm columns). So that is one row(in example I gave it is table1.gm and table2.gm ... ) for each one of that .gm values which are same in every table, not just one row all together. Solutions Nick and Salim provided works but I did not solved problem.
EDIT: SOLVED! after implementing solutions by Nick I just neded to add GROUP BY cntrs_reper.gm_company_no, cntrs_reper.date.
And that's it. For every row in one table enties with highest date and time from others!! Thanks to all.
EDIT. If this can help this is full query:
SELECT cntrs_gm.gm_company_no AS company_c_g,
bns_gms.ded_bns AS ded_bns_gms,
bns_gms.no_ded_bns AS no_ded_bns_gms,
bns_gms.wag_ded_bns AS wag_ded_bns_gms,
cntrs_gm.cur_credit AS cur_credit_c_g,
cntrs_gm.cdrop AS cdrop_c_g,
cntrs_gm.total_jp AS total_jp_c_g,
cntrs_gm.games AS games_c_g,
cntrs_gm.wgames AS wgames_c_g,
cntrs_gm.doors AS doors_c_g,
cntrs_gm.power AS power_c_g,
cntrs_gm.total_in AS total_in_c_g,
cntrs_gm.total_out AS total_out_c_g,
cntrs_gm.total_acc AS total_acc_c_g,
cntrs_gm.total_bet AS total_bet_c_g,
cntrs_gm.total_win AS total_win_c_g,
cntrs_gm.total_bonus AS total_bonus_c_g,
cntrs_gm.date AS date_c_g,
cntrs_reper.gm_company_no AS company_reper,
bns_reper.ded_bns AS ded_bns_reper,
bns_reper.no_ded_bns AS no_ded_bns_reper,
bns_reper.wag_ded_bns AS wag_ded_bns_reper,
cntrs_reper.cur_credit AS cur_credit_reper,
cntrs_reper.cdrop AS cdrop_reper,
cntrs_reper.total_jp AS total_jp_reper,
cntrs_reper.games AS games_reper,
cntrs_reper.wgames AS wgames_reper,
cntrs_reper.doors AS doors_reper,
cntrs_reper.power AS power_reper,
cntrs_reper.total_in AS total_in_reper,
cntrs_reper.total_out AS total_out_reper,
cntrs_reper.total_acc AS total_acc_reper,
cntrs_reper.total_bet AS total_bet_reper,
cntrs_reper.total_win AS total_win_reper,
cntrs_reper.total_bonus AS total_bonus_reper,
cntrs_reper.date AS date_reper,
cntrs_reper.time AS time_reper,
bns_reper.time AS time_c_g,
gms_cfg.gm_no AS machine_id,
gms_cfg.denom_cin AS machine_cin
FROM bns_gms
INNER JOIN cntrs_gm
ON bns_gms.gm_company_no = cntrs_gm.gm_company_no AND bns_gms.date = cntrs_gm.date
INNER JOIN bns_reper
ON cntrs_gm.gm_company_no = bns_reper.gm_company_no
INNER JOIN cntrs_reper
ON bns_reper.gm_company_no = cntrs_reper.gm_company_no AND bns_reper.date = cntrs_reper.date
INNER JOIN gms_cfg
ON cntrs_reper.gm_company_no = gms_cfg.gm_no
WHERE bns_reper.date IN(
SELECT MAX(DATE(bns_reper.date)) FROM bns_reper WHERE bns_reper.time IN(
SELECT MAX(TIME(bns_reper.time)) FROM bns_reper
)
)
AND cntrs_reper.date IN(
SELECT MAX(DATE(cntrs_reper.date)) FROM cntrs_reper WHERE cntrs_reper.time IN(
SELECT MAX(TIME(cntrs_reper.time)) FROM cntrs_reper
)
)
ORDER BY cntrs_gm.gm_company_no
DB example
bns_gms
bns_reper
cntrs_gm
cntrs_reper
gms_cfg
The problem with your current query is that it will select all rows where table1.date is the latest date on which the highest time occurs, which may well be more than one e.g. for data such as
id date time
1 2018-03-30 18:40
2 2018-03-31 12:20
3 2018-03-31 19:20
Your WHERE clause:
table1.date IN(
SELECT MAX(table1.date) FROM table1 WHERE table1.time IN(
SELECT MAX(table1.time) FROM table1
)
will select rows with id=2 and id=3 as they both have date = '2018-03-31' which is when the maximum time occurs.
What you want to do is select the row which has the latest time on the latest date, for which you could use
table1.date = (SELECT MAX(date) FROM table1) AND
table1.time = (SELECT MAX(time) FROM table1 WHERE date = (SELECT(MAX(date) FROM table1))
By using aliasing, that can be simplified (since we already know table1.date = MAX(date) FROM table1) to
table1.date = (SELECT MAX(date) FROM table1) AND
table1.time = (SELECT MAX(time) FROM table1 AS t1 WHERE t1.date = table1.date)
I don't have MySQL but here is the general idea you can use. I don't have enough points to write a comment so I am responding as a reply. Essentially make a subquery/inline view for each table to select max of a column, then join those subqueries/inline views together.
Here is Oracle syntax. You can convert it to ANSI syntax.
select table1.column1, table2.column2,table3.column3
from
(select id1, max(column1) as column1 from table1 group by id1) as table1
(select id2, max(column2) as column2 from table2 group by id2) as table2
(select id3, max(column3) as column3 from table3 group by id3) as table3
where
table1.id1 = table2.id2
and table1.id1 = table3.id3
;

Get data where MAX(date) < X

I have two tables with a one-to-many relationship.
Table1
ID name email
Table2
ID table1_ID date
I need to get all the data from Table1 where :
MAX(date) from Table2 < "2016-01-01"
This doesn't work. Max is considered as "invalid" in where clause. What I did was :
SELECT Table1.name, Table1.email, tmp.maxdate
FROM Table1
JOIN ( SELECT MAX(date) maxdate, table1_ID
FROM Table2
GROUP BY table1_ID ) as tmp
ON tmp.table1_ID = table1.id
WHERE tmp.maxdate < "2016-01-01"
AND (other conditions)
So this works. BUT I think the performance is going to be awful - explain shows that all the Table2 is being read, and this table will grow a lot.
Any idea on how I could do it otherwise, or how to improve my current query performances ?
Try:
SELECT Table1.name, Table1.email, tmp.maxdate
FROM Table1
INNER JOIN ( SELECT MAX(date) maxdate, table1_ID
FROM Table2
GROUP BY table1_ID
HAVING maxdate > "2016-01-01" ) as tmp
ON tmp.table1_ID = table1.id
WHERE
AND (other conditions)
Before, you just bringing back everyone from Table2 and join it with Table1. This will knock off all those without the maxdate > "2016-01-01" and do join on it with Table1.
First of all, don't think , test it by your self and check it.
Secondly, you can try using EXISTS() which might be slightly faster becuase you can filter Table2 and not use a GROUP BY clause :
SELECT * FROM Table1 t1
WHERE EXISTS(SELECT 1 FROM Table2 t2
WHERE t2.date > "2016-01-01"
AND t1.id = t2.table1_id
AND <Other Conditions>)
You can also add table2.date > "2016-01-01" inside your sub query.
In addition, consider adding the following indexes:
Table1(id,name,email)
Table2(table1_id,date)
Note that I recommend these indexes based on the query you provided, if there are extra conditions this indexes might not be complete.

How to combine 2 columns from 2 tables and subtract duplicates

I have 2 tables t1 and t2. Each have a customer ID column. What I am looking for is to join the 2 columns and SUBTRACT the duplicates.
My EG:
Table1 and Table2 with the IDs for each
I have tried a union query. The result I am left with is ID = 1,2,3,4,5,6,7,8,9,10. Where, what I'm after is subtracting 1-5 from Table2 and the result = 6,7,8,9,10.
I hope that makes sense and that someone is able to help. Sorry if this is a bit too simple compared to what you're all used to.
In SQL Server you can use the EXCEPT operator:
select ID
from Table2
except
select ID
from Table1
Mysql does not support it though. Using a an in clause or a left join would work in both servers:
--Using In clause
SELECT ID
FROM Table2
WHERE ID NOT IN
(
SELECT ID
FROM Table1
);
--Using join
SELECT Table2.ID
FROM Table2
left join Table1
on Table2.ID = Table1.ID
where Table1.ID is null
Use left outer join
select * from t1 left outer join t2 on t1.customerid = t2.customerid

Select both ID columns when using UNION and GROUP BY

I'm desperate with this query. I have two tables table1 and table2, tables are identical but they have different data. I'm trying to remove duplicities by columns code and manufacturer. To do that I need in final result ID from table1 ID from table2 and also columns code and manufacturer
SELECT * FROM (
SELECT id,code,manufacturer FROM table1 WHERE manufacturer = 1
UNION SELECT id,code,manufacturer FROM table2 WHERE manufacturer = 1
) AS t GROUP BY code HAVING COUNT(*) > 1
But in result i got only values from table1. It's OK but I just need to get there id from table2 too. Please can anyone give me some tips how to do this ?
You have two basic problems:
Problem 1:
You are using UNION when you should be using UNION ALL, because UNION removes duplicates!
Problem 2:
This isn't the right way to go about the problem. You should be using a simple join, not a union.
Try this:
SELECT
t1.id as table1_id,
t2.id as table2_id,
t1.code,
t1.manufacturer
FROM table1 t1
JOIN table2 t2
ON t2.code = t1.code
AND t2.manufacturer = t1.manufacturer
WHERE manufacturer = 1 -- this WHERE clause is optional
Your use of the WHERE clause is a little odd - consider removing it to get all duplicates from all manufacturers.

SQL - joining data

Is it possible to get 1 result where I require data from 3 tables.
First table: I will need to grab all the fields (1 row found by a primary key)
Second table: I will need to grab the field 'username' (connected to first table by 'master_id')
Third table: I will need to grab the latest added row with the associated master_id key (table has 'date', 'master_id', 'previous_name').
select top 1 first.*, second.username, third.*
from first
inner join second on first.id = second.master_id
inner join third on first.id = third.master_id
order by
third.date desc
As always there are dozens of ways to skin a cat, I'm not sure if this is optimized as the subquery methods, but it should work.
You can join the three tables together. Then, you can use a "filter" join to keep only the latest Table3 row:
select *
from Table1 t1
join Table2 t2
on t2.master_id = t1.master_id
join Table3 t3
on t3.master_id = t1.master_id
join (
select master_id
, max(date) as max_date
from Table3
group by
master_id
) as filter
on t3.master_id = filter.master_id
and t3.date = filter.max_date
You'll need a correlated subquery for that third table.
SELECT t1.*, username, date, previous_name
FROM FirstTable t1
INNER JOIN SecondTable t2 ON t1.master_id=t2.master_id
INNER JOIN
(SELECT master_id, date, previous_name
FROM ThirdTable AS t3_1
WHERE date = (
SELECT MAX(date)
FROM ThirdTable AS t3_2
WHERE t3_2.master_id=t3_1.master_id)) q1 ON q1.master_id=t1.master_id;
NOTE: Untested.