Take results within two dates - php

I have tried various solutions here on the site but unfortunately I did not succeed. I would need that this query that already takes the results with the highest value of the field "vote_up", could also take a second additional value. It picks up the results within a given date (posted_date), a start date and a finish date.
SELECT *
FROM stories S
LEFT JOIN categories C
ON C.category_id = S.c_id
LEFT JOIN (SELECT voted_story,
voter_ip
FROM votes
WHERE voter_ip = '$ip_address'
GROUP BY voted_story) V
ON V.voted_story = S.story_id
WHERE S.story_active = 1
ORDER BY S.vote_up DESC
LIMIT 0, 16
Adding just for clarification, my difficulty is the syntax that makes me errors especially I do not know how to insert two "where" in the same query.

You can add additional WHERE condition clauses for checking posted date as-well.
SELECT *
FROM stories S
LEFT JOIN categories C
ON C.category_id = S.c_id
LEFT JOIN (SELECT voted_story, voter_ip
FROM votes
WHERE voter_ip = "$ip_address"
GROUP BY voted_story) V
ON V.voted_story = S.story_id
WHERE
S.story_active = 1 AND
S.posted_date > "$startDate" AND
S.posted_date < "$endDate"
ORDER BY S.vote_up DESC
LIMIT 0, 16

Related

EXISTS query optimization on mysql query

I have a big data problem with MySQL.
I have:
a users table with 59033 rows, and
a user_notes table with 8753 rows.
But when I search which users have user note in some dates.
My query like this :
SELECT u.*, rep.name as rep_name FROM users as u
LEFT JOIN users as rep on rep.id = u.add_user
LEFT JOIN authorization on authorization.id = u.authorization
LEFT JOIN user_situation_list on user_situation_list.user_situation_id = u.user_situation
WHERE
EXISTS(
select * from user_notes
where user_notes.note_user_id = u.id AND user_notes.create_date
BETWEEN "2017-10-20" AND "2017-10-22"
)
ORDER BY u.lp_modify_date DESC, u.id DESC
Turn it around -- find the ids first; deal with the joins later.
SELECT u.*,
( SELECT rep.name
FROM users AS rep
WHERE rep.id = u.add_user ) AS rep_name
FROM (
SELECT DISTINCT note_user_id
FROM user_notes
WHERE create_date >= "2017-10-20"
AND create_date < "2017-10-20" + INTERVAL 3 DAY
) AS un
JOIN users AS u ON u.id = un.note_user_id
ORDER BY lp_modify_date DESC, id DESC
Notes
No GROUP BY needed;
2 tables seem to be unused; I removed them;
I changed the date range;
User notes needs INDEX(create_date, note_user_id);
Notice how I turned a LEFT JOIN into a subquery in the SELECT list.
If there can be multiple rep_names, then the original query is "wrong" in that the GROUP BY will pick a random name. My Answer can be 'fixed' by changing rep.name to one of these:
MAX(rep.name) -- deliver only one; arbitrarily the max
GROUP_CONCAT(rep.name) -- deliver a commalist of names
Rewriting your query to use a JOIN rather than an EXISTS check in the where should speed it up. If you then group the results by the user.id it should give you the same result:
SELECT u.*, rep.name as rep_name FROM users as u
LEFT JOIN users as rep on rep.id = u.add_user
LEFT JOIN authorization on authorization.id = u.authorization
LEFT JOIN user_situation_list on user_situation_list.user_situation_id = u.user_situation
JOIN user_notes AS un
ON un.note_user_id
AND un.create_date BETWEEN "2017-10-20" AND "2017-10-22"
GROUP BY u.id
ORDER BY u.lp_modify_date DESC, u.id DESC

Grouping and chunking by value in MySQL

I have a simple raw SQL query which is returning data like this:
SELECT
a.id, count(ah.id) as count
FROM
`table_name_one` as a
JOIN
`table_name_two` as ah
ON
a.id=ah.answer_id
WHERE
a.user_id = 7178
AND a.created_at BETWEEN '2017-05-01' AND '2017-05-28'
GROUP BY a.id
I want to group this result by two groups.
Group One will contain data when revisionCount == 1 and Group Two will contain rest of the data. I can do this by using a loop but if I can do this by SQL query it will be good for me.
Can anybody help me out from here?
If you want to construct a list of the two sets of values, you can do so in one query:
SELECT GROUP_CONCAT(CASE WHEN cnt = 1 THEN id END) as id_1s,
GROUP_CONCAT(CASE WHEN cnt > 1 THEN id END) as id_2plus
FROM (SELECT a.id, count(ah.id) as cnt
FROM `table_name_one` a JOIN
`table_name_two` as ah
ON a.id = ah.answer_id
WHERE a.user_id = 7178 AND
a.created_at BETWEEN '2017-05-01' AND '2017-05-28'
GROUP BY a.id
) a;
This puts the values into comma-delimited lists. MySQL by default limits the length to 1,024 characters. If the lists are longer, then you can run the query twice to generate each . . . or just order by cnt and check the value when you are reading the return values.
You can use having clause to achieve this. I would recommend you to read online for more info on having.
Below query will give you all records with count exactly as 1:
SELECT
a.id, count(ah.id) as count
FROM
`table_name_one` as a
JOIN
`table_name_two` as ah
ON
a.id=ah.answer_id
WHERE
a.user_id = 7178
AND a.created_at BETWEEN '2017-05-01' AND '2017-05-28'
GROUP BY a.id
HAVING count(ah.id) = 1
You can tweak the having condition in the above query to get the second set.
I assume you need 2 result sets as arrays in php, for which you need to write 2 queries separated by semicolon.
Query 1 where revision count =1;
Query 2 where revision count >1;
Hope this helps

Two seemingly identical queries returning different results when using count (pagination)

I am running two queries to my database for pagination reasons. As such, each query is nearly identical. My COUNT(*) query is not returning the number of results that the non-count query is. I'm baffled as to why this is the case. The queries are below.
SELECT p.host_id, p.rating_support, p.rating_tech, MAX(p.rating_overall) AS rating_overall, p.publish_rating, h.name, prices.price, prices.term_duration
FROM plans p
INNER JOIN hosts AS h ON h.id = p.host_id
INNER JOIN (SELECT plan_id, price, term_duration FROM prices WHERE price > 0 AND price < 50 AND term_duration = 1) prices ON prices.plan_id = p.id
WHERE p.published = 1 AND h.published = 1
GROUP BY p.host_id
ORDER BY rating_overall desc LIMIT 0, 12
SELECT COUNT(*) AS count
FROM plans p
INNER JOIN hosts AS h ON h.id = p.host_id
INNER JOIN (SELECT plan_id, price, term_duration FROM prices WHERE price > 0 AND price < 50 AND term_duration = 1) prices ON prices.plan_id = p.id
WHERE p.published = 1 AND h.published = 1
GROUP BY p.host_id
I'm not an expert at MySQL. Besides the count not providing the correct number of results, the non-count query works perfectly.
Any light on this problem would be great.
With the help of Dems' comment (hunt down and upvote him somewhere :), I created this query. Notice that I removed the subquery, because it seemed unnecessary:
SELECT
COUNT( DISTINCT p.host_id )
FROM plans p
INNER JOIN hosts h ON h.id = p.host_id
INNER JOIN prices ON prices.plan_id = p.id
AND prices.price > 0
AND prices.price < 50
AND prices.term_duration = 1
WHERE p.published = 1
AND h.published = 1
My original answer:
To get the number of total row, you have to wrap the GROUP BY query into an outer SELECT:
SELECT COUNT(*)
FROM (
SELECT NULL -- we are just counting, so we need no actual data -> a bit faster
FROM plans p
INNER JOIN hosts h ON h.id = p.host_id
INNER JOIN prices ON prices.plan_id = p.id
AND prices.price > 0
AND prices.price < 50
AND prices.term_duration = 1
WHERE p.published = 1
AND h.published = 1
GROUP BY p.host_id
) AS all_rows_without_data
Or you could use SQL_CALC_FOUND_ROWS + FOUND_ROWS()
http://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_found-rows
A SELECT statement may include a LIMIT clause to restrict the number
of rows the server returns to the client. In some cases, it is
desirable to know how many rows the statement would have returned
without the LIMIT, but without running the statement again. To obtain
this row count, include a SQL_CALC_FOUND_ROWS option in the SELECT
statement, and then invoke FOUND_ROWS() afterward:
First, simply select the required rows, but add SQL_CALC_FOUND_ROWS:
SELECT SQL_CALC_FOUND_ROWS
p.host_id, p.rating_support, p.rating_tech,
MAX(p.rating_overall) AS rating_overall,
p.publish_rating, h.name, prices.price, prices.term_duration
FROM plans p
INNER JOIN hosts AS h ON h.id = p.host_id
INNER JOIN prices ON prices.plan_id = p.id
AND prices.price > 0
AND prices.price < 50
AND prices.term_duration = 1
WHERE p.published = 1 AND h.published = 1
GROUP BY p.host_id
ORDER BY rating_overall desc
LIMIT 0, 12;
Second, get the number of rows that would have been returned if there weren't a LIMIT statement in the first query:
SELECT FOUND_ROWS();
Update: SQL_CALC_FOUND_ROWS + FOUND_ROWS() doesn't seem very reliable, always returs zero for unknown reason (not just me: FOUND_ROWS() keeps returning 0 ):
http://sqlfiddle.com/#!2/7304d/8
The result of your second query would return the same number of rows, but the first row won't give you back the total.
The results would give the count for each group per row:
3
5
1
6
etc.
etc.
To get the result into one row, use COUNT(DISTINCT p.host_id) or array_sum() in PHP on the full result set.

mysql has all values

This is relating to my last question mysql query with AND, OR and NOT
Instead of editing the question, I am asking a new one because the question is only part of the previous question with an alteration.
I am looking to do a mysql query that returns me all articles that have all required topics.
Article
id
....
Topic
id
....
ArticleTopics
article_id
topic_id
type
something that would effectively do:
SELECT * FROM Article LEFT JOIN ArticleTopics ON Article.id = ArticleTopics.article_id
WHERE ArticleTopics.topic_id HAS ALL (these topics)
Is this possible? What is the best approach for this?
Several of the other answers suggest using aliases on the child table for each filter clause - this may not be very efficient or scale well.
Consider:
SELECT x.*
FROM Article x INNER JOIN
(SELECT t.article_id, COUNT(t.article_id)
FROM articleTopics t
WHERE t.topic_id IN ([your_list_of_topics])
GROUP BY t.article_id
HAVING COUNT(t.article_id)>=[number of elements in [your_list_of_topics]]
ORDER BY COUNT(t.article_id) DESC
LIMIT 0,100) AS ilv
ON x.id=ilv.article_id
Another advantage of this approach is that the structure of the query doesn't need to change with the number of topics you are searching for - you could even put them in a temporary table and perform a join instead of using the ' IN (...)' literal.
You'd need to try it out to see which query behaves better.
That is done using multiple joins with the same table.
To select all articles that have topics with ID 1, 2 and 3, you need to do:
SELECT * FROM Article a
INNER JOIN ArticleTopics at1 ON a.id = at1.article_id AND at1.topic_id = 1
INNER JOIN ArticleTopics at2 ON a.id = at2.article_id AND at2.topic_id = 2
INNER JOIN ArticleTopics at3 ON a.id = at3.article_id AND at3.topic_id = 3
// EDIT
Fixed it. Added table aliasses; I must have been working with good ORM solutions for too long...
SELECT
*
FROM
Article
WHERE
(SELECT COUNT(*) FROM ArticleTopics
WHERE Article.id = ArticleTopics.article_id AND
ArticleTopics.topic_id=1) > 0 AND
(SELECT COUNT(*) FROM ArticleTopics
WHERE Article.id = ArticleTopics.article_id AND
ArticleTopics.topic_id=2) > 0 AND
(SELECT COUNT(*) FROM ArticleTopics
WHERE Article.id = ArticleTopics.article_id AND
ArticleTopics.topic_id=3) > 0 AND
...

PHP/MySQL Query In a loop, how to append to just one query?

I have a query to pull all articles out of the database..
$get_articles = $db->query("
SELECT *, a.id AS id, s.type AS type FROM ".$prefix."articles a
LEFT JOIN ".$prefix."sources s ON (s.id = source_id)
WHERE a.type!='trashed' ORDER BY a.timestamp DESC LIMIT $start, $end");
Within the loop of this query, I do then do another query on the same table to find related articles to the 'title' of the article, stored as '$related_phrase'. The query within the loop is:
// get related articles to this entry
$get_related = $db->query("
SELECT *, a.id AS id, MATCH (title, description) AGAINST ('$related_phrase') AS score FROM ".$prefix."articles a
LEFT JOIN ".$prefix."sources s ON (s.id = source_id) WHERE a.type!='trashed' AND MATCH (title, description) AGAINST ('$related_phrase') AND a.id!='$articles[id]' HAVING score > 7
ORDER BY a.timestamp DESC LIMIT 0, 3");
This basically means we have a query in a loop which is causing the pages to load very slowly.
What we want to do, is bring the query from within the loop, in the main query, so it's all working within one query, if that's possible?
Any help very much appreciated!
I don't think you would gain much speed by merging the two queries.
One thing you could try is to get a list of all articles and DISTINCT searchphrases (in e.g. temptable), and then build a query to get all related articles in one single go. Lastly match up related articles with the article list.
try this:
$articles_and_related = $db->query("
SELECT *
FROM ".$prefix."articles art
LEFT JOIN (
SELECT * FROM ".$prefix."articles x
WHERE
score > 7
AND x.type != 'trashed'
AND x.id != art.id
AND MATCH(x.title, x.description) AGAINST (art.title)
LIMIT 3
) rel
LEFT JOIN ".$prefix."sources s2 ON (s2.id = rel.source_id)
LEFT JOIN ".$prefix."sources s ON (s.id = art.source_id)
WHERE
art.type!='trashed'
ORDER BY art.timestamp DESC LIMIT $start, $end");

Categories