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.
Related
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.
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
;
I need to query a date with a value between two other dates that come from another table. Something like this:
select * from table1 where date_table1 BETWEEN (select date1,date2 from table2 where id=1)
How can I do that in SQl?
Just use join or exists. As you have written it:
select t1.*
from table1 t1
where exists (select 1
from table2 t2
where t1.date between t2.date1 and t2.date2
);
I am not sure what you are trying to do but the way you phrased it the question seems incomplete. Based on what you have provided, I think something like this will work:
select t1.*
from table1 t1
inner join table2 t2 on t2.date1 <= t1.date_table1 and t1.date_table1 < t2.date2
where t2.somefield = [somelimitingcondition]
This should work provided each sub-query returns exactly one value
SELECT * from table1 t1
WHERE t1.date_table1
BETWEEN
(SELECT t2.date1 from table2 t2 WHERE t1.id=1)
AND (SELECT tt2.date2 from table2 tt2 WHERE t1.id=1);
Looking for away to join 2 tables and keeping all records from the left. The problem is that one of the on statement does not exist in the the other table
Example
Table1 ID, ClassID, Date, Amount
Table2 ID, CLassID, Date, Score
Example statement
select
Table1.ID,
Table1.ClassID,
Table1.Date,
Table1.Amount,
Table2.Score
from Table1
left join Table2 on Table2.ID = Table1.ID
and Table2.ClassID = Table1.ClassID
and Table2.Date = Table1.Date
The Problem is Table1 has an ClassID that Table2 does not have and it is excluding that from the results. If I removed the ClassID as a qualification, I get a ton of duplicates
Your missing value will be null for Score so you will have to deal with that however you like, but this will do the job.
select
Table1.ID,
Table1.ClassID,
Table1.Date,
Table1.Amount,
( SELECT Score
FROM Table2
WHERE ID = Table1.ID
and ClassID = Table1.ClassID
and Date = Table1.Date ) AS 'Score'
from Table1
I'm not a SQL guy, and I've just tried every JOIN MySQL offers.
I primarily need data from view1 using WHERE, ORDER BY, LIMIT with offset (the data is incorrect without those conditions.)
view1 is not limited by view2
Data also needs to be pulled from view2, but view2 has its' own WHERE (or whatever's best) condition
view2 may not have corresponding data, but it shouldn't be grabbed without being linked to view1 (NULLs are just fine!)
I'll just give you the two SELECTs that work for me rather than the hashes I've come up with.
For view1:
SELECT * FROM view1
WHERE column1 IS NULL OR column1 = 1
ORDER BY dateColumn DESC LIMIT index1, count1;
(index1 and count1 are INs i use for a stored proc)
For view2:
SELECT * FROM table2
WHERE column1 = ? AND table1_id IN (
SELECT id FROM view1
WHERE column1 IS NULL OR column1 = 1
ORDER BY dateColumn DESC LIMIT index1, count1
)
GROUP BY table1_id
(? is any arbitrary value i put in)
I'm at wits end. I have no idea how to fuse these two.
Specifics
Please note that the IN for view2 is almost identical to the view1 query. Thanks!
You can write a subquery for table2 and add WHERE clause there, also you can add WHERE clause at the ent of the query -
SELECT * FROM table2 t2
JOIN (
SELECT id FROM table1
WHERE column1 IS NULL OR column1 = 1
ORDER BY dateColumn DESC
LIMIT index1, count1
) t1
ON t1.id = t2.table1_id
WHERE
t2.column1 = ?
GROUP BY
t2.table1_id
In this example I used JOIN instead of WHERE IN condition.
How about this query:
Select t1.id
FROM table1 t1
JOIN table2 t2 ON t1.id = t2.table1_id
WHERE t1.column1 IS NULL OR t1.column1 = 1
i think it should help you
Select TableName1.id
FROM TableName1
JOIN TableName2 ON TableName1.id = TableName2.id
WHERE TableName1.column1 IS NULL OR TableName1.column1 = 1