I have 3 SQL tables.
Games wo_tenis_partidos
Selections wo_tenis_pronosticos
Quotas wo_tenis_cuotas_ha2
I need to get all rows with two conditions:
Condition 1: where CUOTA1 >= CUOTAMIN1 or CUOTA2 >= CUOTAMIN2
Condition 2: Add all rows with same ID_PARTIDO that appeared in Condition 1
The condition 1 is easy:
SELECT *
FROM wo_tenis_partidos a
INNER JOIN wo_tenis_cuotas_ha2 c ON c.id_partido = a.id
INNER JOIN wo_tenis_pronosticos p ON p.id_partido = a.id
WHERE
a.resultado IS NULL
AND (c.cuota1 >= p.cuotamin1 OR c.cuota2 >= p.cuotamin2)
But to add the condiciton 2 I need to do this:
SELECT *
FROM wo_tenis_partidos a
INNER JOIN wo_tenis_cuotas_ha2 c ON c.id_partido = a.id
INNER JOIN wo_tenis_pronosticos p ON p.id_partido = a.id
WHERE a.id IN (
SELECT a.id
FROM wo_tenis_partidos a
INNER JOIN wo_tenis_cuotas_ha2 c ON c.id_partido = a.id
INNER JOIN wo_tenis_pronosticos p ON p.id_partido = a.id
AND a.resultado IS NULL
AND (c.cuota1 >= p.cuotamin1 OR c.cuota2 >= p.cuotamin2)
)
It's the correct way? there is another more optimized alternative?
No need of INNER JOIN in first query and use EXISTS instead of IN clause for better performance
SELECT *
FROM wo_tenis_partidos aa
INNER JOIN wo_tenis_cuotas_ha2 c ON c.id_partido = aa.id
INNER JOIN wo_tenis_pronosticos p ON p.id_partido = aa.id
WHERE EXISTS (
SELECT 1
FROM wo_tenis_partidos a
INNER JOIN wo_tenis_cuotas_ha2 c ON c.id_partido = a.id
INNER JOIN wo_tenis_pronosticos p ON p.id_partido = a.id
AND a.resultado IS NULL
AND (c.cuota1 >= p.cuotamin1 OR c.cuota2 >= p.cuotamin2)
WHERE aa.id =a.id
)
I have this query and I am getting error #1066 - Not unique table/alias: 'components'. What seems to be the issue?
SELECT COUNT(*) FROM `products`, `components`, `tradeNames`
INNER JOIN `componentsMap` ON componentsMap.product_id = product.id
INNER JOIN `components` ON componentsMap.component_id = components.id
INNER JOIN `tradeNamesMap` ON .tradeNamesMap.product_id = products.id
INNER JOIN `tradeNames` ON tradeNamesMap.tradeName_id = tradeNames.id
WHERE (((((LOWER(inci) LIKE '%abies%')
OR (trade_name.LOWER(name) LIKE '%abies%'))
OR (components.LOWER(no_cas)='abies'))
OR (components.LOWER(no_einecs)='abies'))
OR (components.LOWER(name)='abies'))
AND (`published`=1)
ORDER BY `trade_name`.`name` DESC
You don't need to list the tables before the INNER JOINs. In fact, simply don't ever use commas in the FROM clause. So:
SELECT COUNT(*)
FROM `products`
INNER JOIN `componentsMap` ON componentsMap.product_id = product.id
INNER JOIN `components` ON componentsMap.component_id = components.id
INNER JOIN `tradeNamesMap` ON tradeNamesMap.product_id = products.id
INNER JOIN `tradeNames` ON tradeNamesMap.tradeName_id = tradeNames.id
WHERE (((((LOWER(inci) LIKE '%abies%')
OR (trade_name.LOWER(name) LIKE '%abies%'))
OR (components.LOWER(no_cas)='abies'))
OR (components.LOWER(no_einecs)='abies'))
OR (components.LOWER(name)='abies'))
AND (`published`=1)
ORDER BY `trade_name`.`name` DESC;
The above query only returns one row because of the COUNT(). The order by suggests that you actually want this information for each trade_name.name. If so, you need a GROUP BY:
SELECT tn.name, COUNT(*)
FROM `products` p INNER JOIN
`componentsMap cm
ON cm.product_id = p.id INNER JOIN
`components` c
ON cm.component_id = c.id INNER JOIN
`tradeNamesMap` tnm
ON tnm.product_id = p.id INNER JOIN
`tradeNames` tn
ON tnm.tradeName_id = tn.id
WHERE ((LOWER(inci) LIKE '%abies%') OR
(tn.LOWER(name) LIKE '%abies%') OR
(c.LOWER(no_cas)='abies') OR
(c.LOWER(no_einecs)='abies') OR
(c.LOWER(name)='abies')
) AND
(`published` = 1)
GROUP BY tn.name
ORDER BY tn.`name` DESC
INNER JOIN `[components]` ON componentsMap.component_id = components.id
AND
SELECT COUNT(*) FROM `products`, [`components`], `tradeNames`
Two components are there.
Just guessing, and untested, but I suspect that something like this would do what you're after...
SELECT n.name
, COUNT(*)
FROM products p
JOIN componentsMap pc
ON pc.product_id = p.id
JOIN components c
ON c.id = pc.component_id
JOIN tradeNamesMap pn
ON pn.product_id = p.id
JOIN tradeNames n
ON n.id = pn.tradeName_id
WHERE
( inci LIKE '%abies%'
OR n.name LIKE '%abies%'
OR 'abies' IN (c.no_cas,c.no_einecs,c.name)
)
AND published = 1
GROUP
BY n.name
ORDER
BY n.name DESC
SELECT s.*,
u.username,
u.fullname,
c.title AS ctitle,
c.description AS cdescription,
sa.attention,
sp.popularity,
COUNT(DISTINCT f.id) AS favorites,
COUNT(DISTINCT st.id) AS stars,
COUNT(DISTINCT v.id) AS views
FROM shots s
INNER JOIN users u ON u.id = s.user_id
INNER JOIN categories c ON c.id = s.cat_id
LEFT OUTER JOIN(
SELECT shot_id, round(AVG(count),2) AS attention
FROM points
WHERE date > DATE_SUB(CURDATE(),INTERVAL 2 DAY)
GROUP BY shot_id
) sa ON sa.shot_id = s.id
LEFT OUTER JOIN(
SELECT shot_id, SUM(count) AS popularity
FROM points
GROUP BY shot_id
) sp ON sp.shot_id = s.id
LEFT OUTER JOIN favorites f ON f.shot_id = s.id
LEFT OUTER JOIN stars st ON st.shot_id = s.id
LEFT OUTER JOIN views v ON v.shot_id = s.id
**WHERE s.library = 1 AND sa.attention > 40
ORDER BY sa.attention DESC
LIMIT 0,50**
GROUP BY s.id
I can't use the sa.attention in a condition and for ordering. Why?
(I removed the marked part, and the query works!)
What do I have to change in my Query? And if you could give a explanation for it, that would be very nice!
You are negating your OUTER JOIN by putting that in your WHERE criteria. Move it to your JOIN and you'll get your NULL records back:
SELECT s.*,
u.username,
u.fullname,
c.title AS ctitle,
c.description AS cdescription,
sa.attention,
sp.popularity,
COUNT(DISTINCT f.id) AS favorites,
COUNT(DISTINCT st.id) AS stars,
COUNT(DISTINCT v.id) AS views
FROM shots s
INNER JOIN users u ON u.id = s.user_id
INNER JOIN categories c ON c.id = s.cat_id
LEFT OUTER JOIN(
SELECT shot_id, round(AVG(count),2) AS attention
FROM points
WHERE date > DATE_SUB(CURDATE(),INTERVAL 2 DAY)
GROUP BY shot_id
) sa ON sa.shot_id = s.id AND sa.attention > 40
LEFT OUTER JOIN(
SELECT shot_id, SUM(count) AS popularity
FROM points
GROUP BY shot_id
) sp ON sp.shot_id = s.id
LEFT OUTER JOIN favorites f ON f.shot_id = s.id
LEFT OUTER JOIN stars st ON st.shot_id = s.id
LEFT OUTER JOIN views v ON v.shot_id = s.id
WHERE s.library = 1
GROUP BY s.id
ORDER BY sa.attention DESC
LIMIT 0,50
A second note, GROUP BY cannot go at the end. I moved that to the correct spot as well.
SELECT a.*, p.*, t.*, r.*, n.*
FROM prop_assigns AS a
LEFT JOIN properties AS p
ON (p.property_id = a.property_id)
LEFT JOIN tasks AS t ON (t.task_id = a.task_id)
LEFT JOIN reminders_tasks AS r ON (a.assign_id = r.assign_id)
LEFT JOIN notes_view AS n ON (p.property_id = n.property_id)
WHERE a.user_id = 3 AND a.task_id <> 0 AND
a.assign_done = 0 AND n.user_id = 3 ORDER BY task_id desc
MySQL a répondu: Documentation
#1052 - Column 'task_id' in order clause is ambiguous
it look long sql any help
The field task_id is in more than one table. You need to specify which you mean:
SELECT a.*, p.*, t.*, r.*, n.*
FROM prop_assigns AS a LEFT JOIN
properties AS p
ON (p.property_id = a.property_id) LEFT JOIN
tasks AS t
ON (t.task_id = a.task_id) LEFT JOIN
reminders_tasks AS r
ON (a.assign_id = r.assign_id) LEFT JOIN
notes_view AS n
ON (p.property_id = n.property_id)
WHERE a.user_id = 3 AND a.task_id <> 0 AND a.assign_done = 0 AND n.user_id = 3
ORDER BY t.task_id desc;
---------^
I have a MySQL query that is currently using a nested select, and I am wondering if it is possible to rewrite the query to not use a nested select, and if so how?
The query is as follows
SELECT
b.id,
b.name,
b.description,
b.order,
b.icon,
b.locked,
u.username AS lastPoster,
p.time AS lastPostTime,
p1.subject AS lastPostTopicSubject,
p2.postscount AS totalPosts,
t1.topicscount AS totalTopics,
p.subject AS lastPostSubject,
t.id AS lastPostTopicId
FROM kf_boards AS b
LEFT JOIN kf_topics AS t ON (t.boardid = b.id)
LEFT JOIN (SELECT posterid, topicid, time, subject
FROM kf_posts
ORDER BY time DESC) AS p ON (p.topicid = t.id)
LEFT JOIN (SELECT subject
FROM kf_posts
ORDER BY time ASC) AS p1 ON (p.topicid = t.id)
LEFT JOIN (SELECT COUNT(id) AS postscount
FROM kf_posts) AS p2 ON (p.topicid = t.id)
LEFT JOIN (SELECT COUNT(id) AS topicscount
FROM kf_topics) AS t1 ON (t.boardid = b.id)
LEFT JOIN kf_users AS u ON (p.posterid = u.id)
WHERE b.categoryid = :catid
GROUP BY b.name
ORDER BY b.order
And the database structure is as follows
Any help would be much appriciated!
Thanks!
Edit: Tried below query, results returned
Results should be as follows
try:
SELECT
b.id, b.name, b.description, b.order, b.icon, b.locked,
u.username AS lastPoster, p.time AS lastPostTime,
p.subject AS lastPostSubject, t.id AS lastPostTopicId
FROM kf_boards AS b
LEFT JOIN kf_topics AS t ON t.boardid = b.id
LEFT JOIN kf_posts AS p ON p.topicid = t.id
LEFT JOIN kf_users AS u ON p.posterid = u.id
WHERE b.categoryid = :catid
GROUP BY b.name
ORDER BY b.order ASC, p.time DESC
UPDATE: bellow is for your new query.
SELECT b.id, b.name, b.description, b.order, b.icon, b.locked,
u.username AS lastPoster, MAX(p.time) AS lastPostTime,
p.subject AS lastPostTopicSubject, count(p.id) AS totalPosts,
count(t.id) AS totalTopics, p.subject AS lastPostSubject,
max(t.id) AS lastPostTopicId
FROM kf_boards AS b
LEFT JOIN kf_topics AS t ON t.boardid = b.id
LEFT JOIN kf_posts AS p ON p.topicid = t.id
LEFT JOIN kf_users AS u ON p.posterid = u.id
WHERE b.categoryid = :catid
GROUP BY b.name, b.id, b.name, b.description, b.order, b.icon,
b.locked, u.username, p.subject
ORDER BY b.order
Here's a solution that might help, however, I have additional suggestions to simplify all querying later via triggers. I'll explain that later.
I'm starting with the inner-most query on just board IDs for category (your parameter) and that the board HAS POSTINGS (not via LEFT-JOIN). From this, I am getting just the maximum post ID per board regardless of topic (just that it must be of a valid board per the joins).
Once I have that, the next query out re-joins to the posts table based on the last post to determine the topic... then re-joins THAT to the posts again on the same topic ID. With that, I can get the FIRST Post ID and total Entries for this topic for the board... all grouped per "board ID".
These are obviously only boards that HAVE AT LEAST 1 Board, but that's not what you want. You want ALL boards regardless of a post. So, I'm back to the beginning to query kf_boards again with same WHERE on category ID = your parameter... THIS gets you all the boards for the category...
NOW, you can left-join to the pre-aggregate query for min/max post and entries count... Then take THAT to left-join to the posts table again but TWICE... Once for the FIRST post (so you can get the initial subject caption, time and whatever else you might care about), and AGAIN for the LAST POST to get ITs time, subject, etc... You already have the total post entries for this topic from the pre-aggregate query. Finally, a left-join on the last post to the users table to see who posted it last.
I've tested the syntax on it and it works, just can't confirm based on actual data.
SELECT
B.ID,
B.Name,
B.Description,
B.Order,
B.Icon,
FP.Subject as FirstPostSubject,
FP.Time as FirstPostTime,
LP.Subject as LastPostSubject,
LP.Time as LastPostTime,
U.UserName as LastPostUserName,
QryPerBoard.PostEntries
from
kf_boards B
LEFT JOIN
( select
PQ1.ID,
PQ1.LastPostID,
MIN( P2.ID ) as FirstPostID,
COUNT(*) as PostEntries
from
( SELECT
B1.ID,
MAX( P1.ID ) as LastPostID
from
kf_boards B1
join kf_topics T1
ON B1.ID = T1.BoardID
join kf_posts P1
ON T1.ID = P1.TopicID
where
B1.CategoryID = 1 <-- Insert your Category Parameter ID here
group by
B1.ID ) as PQ1
LEFT JOIN kf_posts P1
ON PQ1.LastPostID = P1.ID
LEFT JOIN kf_posts P2
ON P1.TopicID = P2.TopicID
group by
PQ1.ID ) QryPerBoard
ON B.ID = QryPerBoard.ID
LEFT JOIN kf_posts FP
ON QryPerBoard.FirstPostID = FP.ID
LEFT JOIN kf_posts LP
ON QryPerBoard.LastPostID = LP.ID
LEFT JOIN kf_users U
ON LP.PosterID = U.ID
where
B.CategoryID = 1 <-- Insert your Category Parameter ID here (second copy for parameter)
Now, how I would adjust to prevent the recursive level of querying, especially for a website. Use triggers. When a POST is created and saved, have a trigger that does a few things...
REVISED THOUGHT on trigger impact.
Update the kf_Boards with the latest TOPIC ID any post was created for the respective board ID so you never have to look for it later, just take whatever is the last and run with it. In addition, update the TOPIC record. Have a column for the FIRST POST, LAST POST and TOTAL POSTS for the topic. If its the first post to the topic, update both first AND last post with the new ID and increment the total post count.
The time to incorporate these triggers to update the "extra" columns would save such complexities for future queries. You'll basically be able to do something like
select
B.*,
LP.Fields, <obviously apply specific fields you want per table>
FP.Fields,
U.Fields
from
kf_boards B
LEFT JOIN kf_topics T
on B.LastTopicID = T.ID
LEFT JOIN kf_posts FP
on T.FirstPostID = FP.ID
LEFT JOIN kf_posts LP
on T.LastPostID = LP.ID
LEFT JOIN kf_users U
on LP.PosterID = U.ID
where
B.CategoryID = 1 <-- your parameterID
It seems possible to remove all the subqueries,
but the query will be more clear if the last post, and the first post on the corresponding
topic is found using subqueries:
SELECT b.id, b.name, b.description, b.sortorder, b.icon, b.locked,
u.username AS lastPoster,
p1.time AS lastpostTime,
p0.subject AS lastPostTopicSubject,
COUNT(DISTINCT p.id) AS totalPosts,
COUNT(DISTINCT t.id) AS totalTopics,
p1.subject AS lastPostSubject,
p1.topicid AS lastPostTopicId
FROM kf_boards b
LEFT JOIN kf_topics t ON t.boardid = b.id
LEFT JOIN kf_posts p ON p.topicid = t.id
LEFT JOIN kf_posts p1
ON p1.time = (SELECT MAX(time) FROM kf_posts p
INNER JOIN kf_topics t
ON p.topicid = t.id
WHERE t.boardid = b.id)
LEFT JOIN kf_users u ON u.id = p1.posterid
LEFT JOIN kf_posts p0
ON p0.time = (SELECT MIN(time) FROM kf_posts p0
WHERE p0.topicid = p1.topicid)
WHERE b.categoryid = :catid
GROUP BY b.id
ORDER BY b.sortorder;
However, the following query, using self joins to find posts that have no related previous/nest post should give the same answer:
SELECT b.id, b.name, b.description, b.sortorder, b.icon, b.locked,
u.username AS lastPoster,
lastpost.time AS lastpostTime,
firstpost.subject AS lastPostTopicSubject,
COUNT(DISTINCT p.id) AS totalPosts,
COUNT(DISTINCT t.id) AS totalTopics,
lastpost.subject AS lastPostSubject,
lastpost.topicid AS lastPostTopicId
FROM kf_boards b
LEFT JOIN kf_topics t ON t.boardid = b.id
LEFT JOIN kf_posts p ON p.topicid = t.id
LEFT JOIN kf_topics lasttopic ON lasttopic.boardid = b.id
LEFT JOIN kf_posts lastpost ON lastpost.topicid = lasttopic.id
LEFT JOIN kf_topics nexttopic ON nexttopic.boardid = b.id
LEFT JOIN kf_posts nextpost -- order posts
ON nextpost.topicid = nexttopic.id -- in same board
AND nextpost.time > lastpost.time -- by time
LEFT JOIN kf_users u ON u.id = lastpost.posterid
LEFT JOIN kf_posts AS firstpost ON firstpost.topicid = lastpost.topicid
LEFT JOIN kf_posts prevpost -- order posts
ON prevpost.topicid = firstpost.topicid -- on same topic
AND prevpost.time < firstpost.time -- by time
WHERE nextpost.id IS NULL -- last post has no next
AND prevpost.id IS NULL -- first post on topic has no previous
AND b.categoryid = :catid
GROUP BY b.id
ORDER BY b.sortorder;
Check the result at http://sqlfiddle.com/#!2/1c042/1/0