Ok, the title is cryptic but I don't know how to sintetize it better.
I have a series of expensive similar SELECT SUM queries that must be executed in sequence.
Example:
SELECT SUM(t2.Field)
FROM Table1 AS t1
INNER JOIN (
SELECT Field FROM Table2
WHERE [list of where]
) AS t2 ON ti.ExtKey = t2.Key
WHERE t1.TheValue = 'Orange'
SELECT SUM(t2.Field)
FROM Table1 AS t1
INNER JOIN (
SELECT Field FROM Table2
WHERE [list of where]
) AS t2 ON ti.ExtKey = t2.Key
WHERE t1.TheValue = 'Apple'
And so on.
I've used the nested inner join because after some test it resulted faster than a plain Join.
The rows selected for Table2 are always the same, or at least the same for session.
There's a way to group all the queries in one to speed up the execution?
I was thinking about using a material view, but this would complicate very much the design and maintenance.
I am no sure about your goal. I have a guess for you:
http://sqlfiddle.com/#!9/af66e/2
http://sqlfiddle.com/#!9/af66e/1
SELECT
SUM(IF(t1.TheValue = 'Orange',t2.Field,0)) as oranges,
SUM(IF(t1.TheValue = 'Apple',t2.Field,0)) as apples
FROM Table1 AS t1
INNER JOIN (
SELECT Field, `key` FROM Table2
) AS t2 ON t1.ExtKey = t2.`key`
# GROUP BY t1.extkey uncomment if you need it
If you can provide raw data sample and expected result that would help a lot.
I think you want a group by:
SELECT t1.TheValue, SUM(t2.Field)
FROM Table1 t1 INNER JOIN
(SELECT Field
FROM Table2
WHERE [list of where]
) t2
ON t1.ExtKey = t2.Key
GROUP BY t1.theValue;
Note that your query doesn't quite make sense, because t2 doesn't have a column called key. I assume this is an oversight in the question.
If you want to limit it to particular values, then use a WHERE clause before the GROUP BY:
WHERE t1.TheValue IN ('Apple', 'Orange', 'Pear')
Related
I have a table id1, id2, type. type is an enumerated value containing a name of another table.
I'd like to preform a join with the name of the table of type.
For example:
switch($type)
case 'table1':
join table1;
break;
case 'table2':
join table2;
break;
How can I achieve this?
You can't do it directly like that... you can do something like this though (not very pretty...):
SELECT
t.id,
t.type,
t2.id AS id2,
t3.id AS id3
FROM t
LEFT JOIN t2 ON t2.id = t.id AND t.type = 't2'
LEFT JOIN t3 ON t3.id = t.id AND t.type = 't3'
ugly way:
Table Types, T1, T2:
SELECT ... FROM Types, T1 , where Types.ID=T1.Types_ID AND Types.TYPE='TABLE1'
UNION
SELECT ... FROM Types, T2 , where Types.ID=T2.Types_ID AND Types.TYPE='TABLE2'
In addition to previous answer:
You can combine the two left joins results by using IF statement:
IF(t.type= 't2', t2.id, t3.id) as type_id
You can see another mysql conditional joins example at mysqldiary.com
I needed to implement such a thing with Laravel Query Builder. I was preparing a library and shouldn't rebuild the whole query, I wanted to utilize Eloquent as much as possible so I could only add a join to the query. This could be a little closer to what you want but also a lot uglier than you would expect:
SELECT
`comments`.*,
`commentable`.`created_at` AS `commentable_created_at`
FROM
`comments`
LEFT JOIN ((
SELECT
*,
CONCAT('post_', `id`) AS `morphed_key`
FROM
`posts`)
UNION (
SELECT
*,
CONCAT('image_', `id`) AS `morphed_key`
FROM
`images`)) AS `commentable` ON
CONCAT(`comments`.`commentable_type`, '_', `comments`.`commentable_id`)= `commentable`.`morphed_key`
The point of using this way is that you are now able to add WHERE clauses like WHERE commentable.owner_id=?
I created a relatively simple query in MySQL to give me a JOIN on three tables based on where first names and last names matched. From there, I wanted to write another query that would then only show me the records that didn't get matched from the JOIN -- but I couldn't quite figure out how to do it. I'm assuming that it has to do with using a subquery involving something like NOT IN and my original query, but I couldn't get it to give me the results I wanted.
This is the work-around I tried to come up with that partially functioned properly:
SELECT *,
if(t2.first=t1.first AND t2.last=t1.last, "Match", "No Match") AS "t2 Match",
if(t3.first=t1.first AND t3.last=t1.last, "Match", "No Match") AS "t3 Match"
FROM t1
LEFT JOIN t2 ON t2.first=t1.first AND t2.last=t1.last
LEFT JOIN t3 ON t3.first=t1.first AND t3.last=t1.last
WHERE if(t2.first=t1.first AND t2.last=t1.last, "Match", "No Match")="No Match"
OR if(t3.first=t1.first AND t3.last=t1.last, "Match", "No Match")="No Match";
I feel like this is something that's fairly simple and straight-forward, but I'm not getting the correct results. Can anybody help?
Thanks!
No match means that the t2 (or t3) columns are populated with Nulls in the results. So you can use IS NULL checks:
SELECT t1.*
FROM t1
LEFT JOIN t2 ON t2.first = t1.first AND t2.last = t1.last
LEFT JOIN t3 ON t3.first = t1.first AND t3.last = t1.last
WHERE t2.first IS NULL
OR t3.first IS NULL ;
And you were right, you can also write the queries using NOT IN (warning: only if the joining columns are not nullable. Otherwise you may have unexpected results):
SELECT t1.*
FROM t1
WHERE (first, last) NOT IN
( SELECT first, last FROM t2 )
OR (first, last) NOT IN
( SELECT first, last FROM t3 )
;
or using NOT EXISTS:
SELECT t1.*
FROM t1
WHERE NOT EXISTS
( SELECT 1
FROM t2
WHERE t1.first = t2.first
AND t1.last = t2.last
)
OR NOT EXISTS
( SELECT 1
FROM t3
WHERE t1.first = t3.first
AND t1.last = t3.last
) ;
SELECT count( t1.id ) , t2.special_value
FROM table_1 AS t1, table_2 AS t2
WHERE t1.`group` = 'val'
AND t2.code = 'val'
returns
count - normal value
special_value - NULL
but
SELECT t2.special_value
FROM table_2 AS t2
WHERE t2.code = 'val'
returns
special_value - another normal value
why first query works wrong?..
currently, i need
(count( t1.id ) + t2.special_value)
This is why you should never use SQL'89 implicit join syntax.
You have no join condition resulting in a cross join.
Rewrite the query using explicit join syntax:
SELECT count( t1.id ) , t2.special_value
FROM table_1 AS t1
INNER JOIN table_2 AS t2 ON (t1.`group` = t2.code) <<-- join condition here
WHERE .... <<-- filter condition here
GROUP BY .... <<-- group by field here
I don't know how table t1 and t2 are linked, so you'll have to tweak this a bit, but that's how it supposed to work.
And please never ever use implicit where joins again.
Remarks
I'm wondering what p.`group` and pp.code are, but I'm guessing you meant to write t1.`group` and t2.code
You only need to escape fields and tablenames in ` backticks if they are reserved words.
Personally all those backticks make me dizzy, but that's just me.
I have the following two tables:
Table1 {T1ID, Name}
Table2 {T2ID, T1ID, Date, Value}
Date is of type DATE.
and I am looking for a SQL query to fetch only the latest value (by Date) for each T1ID for which the Name matches a specific string.
SELECT`Table2`.`T1ID`,
`Table2`.`Value`,
`Table2`.`Date`,
`Table1`.`Name`,
FROM `Table1`
INNER JOIN `Table2` ON `Table2`.`T1ID` = `Table1`.`T1ID`
WHERE `Table1`.`Name` LIKE 'Smith'
but this returns the value for several dates for the same T1ID.
How do I get only the latest value by Date?
Edit:
I am using MySQL 5.5.8
If I've understodd the question correctly:
Assuming MySQL:
SELECT`Table2`.`T1ID`,
`Table2`.`Value`,
`Table2`.`Date`,
`Table1`.`Name`
FROM `Table1`
INNER JOIN `Table2` ON `Table2`.`T1ID` = `Table1`.`ID`,
(SELECT T1ID, MAX(Date) AS 'Date' FROM Table2 GROUP BY T1ID) Table3
WHERE
`Table3`.`T1ID` = `Table2`.`T1ID`
AND
`Table3`.`Date` = `Table2`.`Date`
AND
`Table1`.`Name` LIKE 'Smith'
EDIT: Updated the code to bring back the correct result set. Removed MSSQL answer as it wasn't relevant
You have two options.
select t1.t1id, max(t1.Name) Name, max(t2.date) Date,
(select Value from table2 t22
where t22.date = max(t2.date) and t22.t1id = t2.t1id) Value
from table1 t1 left join table2 t2 on t1.t1id = t2.t1id
where Name like '%Smith%'
group by t2.t1id order by 2
OR
select mx.t1id, mx.Name, mx.Date, t2.Value
from
(
select t1.t1id, max(t1.Name) Name, max(t2.date) Date
from table1 t1 left join table2 t2 on t1.t1id = t2.t1id
where Name like '%Smith%'
group by t2.t1id
) mx left join table2 t2 on (t2.t1id = mx.t1id and t2.date = mx.date)
order by 2
Both will produce the same result. The first one takes less code but you might have performance issues with a huge set of data. The second one takes a little more code, but it is also a little more optimized. Notes on the JOIN option:
If you go LEFT JOIN (as the example shows), items in Table1 with no correspondent records on Table2 will be displayed in the result, but the values in columns Date and Value will be NULL
If you go INNER JOIN, items in Table1 with no correspondent records on Table2 will not be displayed.
EDIT
I missed one of the requirements, which was the Name matching a specific string. The code is now updated. The '%' acts like a wildcard, so it will match names like 'Will Smith' and 'Wail Smithers'. If you want a exact match, remove the wildcards ('%').
Add this to your SQL:
ORDER BY 'Date' DESC LIMIT 1
I have a table id1, id2, type. type is an enumerated value containing a name of another table.
I'd like to preform a join with the name of the table of type.
For example:
switch($type)
case 'table1':
join table1;
break;
case 'table2':
join table2;
break;
How can I achieve this?
You can't do it directly like that... you can do something like this though (not very pretty...):
SELECT
t.id,
t.type,
t2.id AS id2,
t3.id AS id3
FROM t
LEFT JOIN t2 ON t2.id = t.id AND t.type = 't2'
LEFT JOIN t3 ON t3.id = t.id AND t.type = 't3'
ugly way:
Table Types, T1, T2:
SELECT ... FROM Types, T1 , where Types.ID=T1.Types_ID AND Types.TYPE='TABLE1'
UNION
SELECT ... FROM Types, T2 , where Types.ID=T2.Types_ID AND Types.TYPE='TABLE2'
In addition to previous answer:
You can combine the two left joins results by using IF statement:
IF(t.type= 't2', t2.id, t3.id) as type_id
You can see another mysql conditional joins example at mysqldiary.com
I needed to implement such a thing with Laravel Query Builder. I was preparing a library and shouldn't rebuild the whole query, I wanted to utilize Eloquent as much as possible so I could only add a join to the query. This could be a little closer to what you want but also a lot uglier than you would expect:
SELECT
`comments`.*,
`commentable`.`created_at` AS `commentable_created_at`
FROM
`comments`
LEFT JOIN ((
SELECT
*,
CONCAT('post_', `id`) AS `morphed_key`
FROM
`posts`)
UNION (
SELECT
*,
CONCAT('image_', `id`) AS `morphed_key`
FROM
`images`)) AS `commentable` ON
CONCAT(`comments`.`commentable_type`, '_', `comments`.`commentable_id`)= `commentable`.`morphed_key`
The point of using this way is that you are now able to add WHERE clauses like WHERE commentable.owner_id=?