Getting rows between 2 given dates (included rows on those dates) - mysql

I know this should be simple, but Its proving to be quite complicated, I have two tables:
USERS id | name | url
COMMENTS id | id_user | text | lang | date(datetime)
And I want to retrieve all records (comments) between this two given dates (both dates included) I have tried in two different ways but they dont work as spected, returning no results where it should:
OPTION A
The following sentence returns nothing, and there are comments and this two comments should appear as the dates are '2014-01-09 16:34:58' and '2014-01-13 10:09:24'
SELECT
comments.text,
users.url,
users.name
FROM comments
JOIN users
ON comments.id_user = users.id
WHERE comments.date BETWEEN '2014-01-13'
AND '2014-01-09'
AND comments.lang = 'es'
ORDER BY comments.date DESC
OPTION B
THe following sentence returns commments written on day '2014-01-09' BUT not the ones written on '2013-01-09'
SELECT
comments.text,
users.url,
users.name
FROM comments
JOIN users
ON comments.id_user = users.id
WHERE comments.date <= '2014-01-13'
AND comments.date > '2014-01-09'
AND comments.lang = 'es'
ORDER BY comments.date DESC
What am I doing wrong?

Change this:
WHERE comments.date BETWEEN '2014-01-13' AND '2014-01-09'
To this:
WHERE comments.date BETWEEN '2014-01-09' AND '2014-01-13 23:59:59'
The MySQL BETWEEN clause expects the min and max parameters to be ordered properly so smaller value must be specified first. Secondly, to include records for 2014-01-13 you need to add the time portion (the time part in datetime does not contain milliseconds so checking for 23:59:59 is sufficient). Alternately you can write:
WHERE comments.date >= '2014-01-09'
AND comments.date < '2014-01-13' + INTERVAL 1 DAY
-- ^---------------------------^ evaluates to 2014-01-14

Instead of BETWEEN use this
SELECT
comments.texto,
usuarios.url,
users.nombre
FROM comments
JOIN usuarios
ON comments.id_usuario = users.id
WHERE DATE(comments.date) <= '2014-01-14'
AND DATE(comments.date) >= '2014-01-09'
AND comments.lang = 'es'
ORDER BY comments.fecha DESC
OR
SELECT
comments.texto,
usuarios.url,
users.nombre
FROM comments
JOIN usuarios
ON comments.id_usuario = users.id
WHERE comments.date <= '2014-01-14 59:59:59'
AND comments.date >= '2014-01-09 00:00:00'
AND comments.lang = 'es'
ORDER BY comments.fecha DESC

Related

Subquery in MySQL with joined tables

I am trying to create a subquery but am failing miserably at it. My query fails with this error...
Operand should contain 1 column(s)
What I want is to find all accounts where the schickUpdatedDate field has a date value within the last 40 days and no not have either 'Initial Waranty' or 'None' as values in the support_c field.
SELECT SQL_CALC_FOUND_ROWS * FROM accounts
LEFT JOIN accounts_cstm ON accounts.id = accounts_cstm.id_c
WHERE schickUpdatedDate BETWEEN NOW() - INTERVAL 40 DAY AND NOW()IN
(SELECT * FROM accounts_cstm WHERE support_c != 'Initial Waranty' OR support_c != 'None')
ORDER BY schickUpdatedDate ASC
All of the fields above are in the accounts_ctsm table, the accounts table if joined for another purpose other than this.
If I understand correctly you just want to show the joined output filtered by 3 criteria. I don't think you need a subquery for it then.
Does this work?
SELECT *
FROM accounts
LEFT JOIN accounts_cstm ON accounts.id = accounts_cstm.id_c
WHERE schickUpdatedDate >= (curdate() - interval 40 day)
and support_c <> 'Initial Waranty'
and support_c <> 'None'
ORDER BY schickUpdatedDate ASC;

Include zeros in SQL count query?

I want to be able to return 0 when I am doing a count, I'd preferably not use joins as my query doesn't use them.
This is my query.
SELECT count( user_id ) as agencyLogins,
DATE_FORMAT(login_date, '%Y-%m-%d') as date
FROM logins, users
WHERE login_date >= '2015-02-10%' AND login_date < '2016-02-11%'
AND logins.user_id = users.id
GROUP BY DATE_FORMAT(login_date,'%Y-%m-%d')
What it does is counts the amount of times a user has logged into the website.
It doesn't count zeros though where as I want to know when there has been no log ins.
Please try using explicit join in the future, more readable and will make you avoid this errors. What you need is a left join:
SELECT t.id,count(s.user_id) as agencyLogins, DATE_FORMAT(s.login_date, '%Y-%m-%d') as date
FROM users t
LEFT OUTER JOIN login s
ON(t.id = s.user_id)
WHERE (s.login_date >= '2015-02-10%' AND s.login_date < '2016-02-11%') or (s.user_id is null)
GROUP BY t.id,DATE_FORMAT(s.login_date,'%Y-%m-%d')
This might be help you out
SELECT SUM(agencyLogins), date FROM (
SELECT count( user_id ) as agencyLogins,
DATE_FORMAT(login_date, '%Y-%m-%d') as date
FROM logins, users
WHERE login_date >= '2015-02-10%' AND login_date < '2016-02-11%'
AND logins.user_id = users.id
GROUP BY DATE_FORMAT(login_date,'%Y-%m-%d')
UNION ALL
SELECT 0,''
) AS A
GROUP BY DATE
I think below SQL useful to you. 2015-02-10% please remove % symbol in that string.
SELECT IF(COUNT(user_id) IS NULL,'0',COUNT(user_id)) as agencyLogins, DATE_FORMAT(login_date, '%Y-%m-%d') as date FROM users left join logins on logins.user_id = users.id
WHERE date(login_date) >= date('2015-02-10') AND date(login_date) <= date('2016-02-11')
GROUP BY DATE_FORMAT(login_date,'%Y-%m-%d')

Rails activerecord : sum, max and joins

I have two models users and posts. An user can votes and views a post
#users
id
name
#posts
id
count_votes
count_views
users_id
created_at
updated_at
I want the user who received the most votes and views on his posts from the last 24 hours. The biggest sum of views and votes win.
WHAT I TRIED
I have this SQL query, it's good but I would like to have the user with the max of votes, this one give me all users and I don't know how to add count_views
select u.name as "Name", sum(p.count_votes)
from posts p
inner join users u on p.user_id = u.id
where p.created_at >= DATE_SUB(NOW(), INTERVAL 1 day)
group by user_id;
ActiveRecord version
Post.select("users.id, sum(posts.count_votes) as nb_votes")
.joins(:user).where("posts.created_at >= ?", 1.day.ago.beginning_of_day)
.group("posts.user_id")
# Results on IRB
=> #<ActiveRecord::Relation [#<Post id: 1>, #<Post id: 3>]>
How can I combine a sum and a max on these two sums ? Is there a way to have an activerecord code or only raw SQL ?
You current query does a grouping on user. So you would get one record for each user in the output. By limiting the output to just 1 record and ordering by votes+views total count, you can get the top user.
Raw SQL:
select u.id, sum(p.count_votes + p.count_views) total
from posts p
inner join users u on p.user_id = u.id
where p.created_at >= DATE_SUB(NOW(), INTERVAL 1 day)
group by u.id
order by total DESC
limit 1 ;
ActiveRecord version: Start from your User model, so you will get the user object in the output instead of Post object, like you have mentioned in the question.
User.select("users.id, sum(posts.count_votes + posts.count_views) as nb_votes")
.joins(:post).where("posts.created_at >= ?", 1.day.ago.beginning_of_day)
.group("posts.user_id").order("nb_votes DESC").limit(1)
ActiveRecord makes sense once you work with an object, in that case you need to have a User object only, so here is your solution below:
sql = %Q(SELECT * FROM users
WHERE id = (SELECT users_id FROM posts
WHERE DATE(created_at) = CURDATE()
ORDER BY count_votes DESC, count_views DESC
LIMIT 1
)
LIMIT 1)
ActiveRecord::Base.connection.execute(sql)

Implementing a WHERE clause within a subquery for the value of a pivot column?

I've successfully managed to perform a few table joins and generate a result using a pivot table (with the aid of Ollie Jones). The sql statement and result are below.
I'd like the results returned to only have rows with values of pivot column 'date' (I think that's the terminology!?) set for today or in the future. From what I can see WHERE date >= CURDATE() should do the job however because 'date' doesn't technically exist I receive an error on execution when added to the WHERE clause at the end of the statement. I'm not sure how to integrate it into my second subquery, any help would be hugely appreciated :)
Thanks in advance
SELECT
content.id as id, content.alias as alias,
(
SELECT modx_site_tmplvar_contentvalues.value FROM modx_site_tmplvar_contentvalues
WHERE modx_site_tmplvar_contentvalues.tmplvarid = 324
AND modx_site_tmplvar_contentvalues.contentid = content.id
) AS featured,
(
SELECT modx_site_tmplvar_contentvalues.value FROM modx_site_tmplvar_contentvalues
WHERE modx_site_tmplvar_contentvalues.tmplvarid = 289
AND modx_site_tmplvar_contentvalues.contentid = content.id
) AS date
FROM modx_site_content AS content
LEFT JOIN
modx_site_tmplvar_contentvalues AS tv_values
ON tv_values.contentid = content.id
WHERE content.parent = 1842
AND content.published = 1
GROUP BY tv_values.contentid
ORDER BY featured DESC, date ASC
One option si to change section of "date" and do it with "left join":
SELECT
content.id as id, content.alias as alias,
(
SELECT modx_site_tmplvar_contentvalues.value FROM modx_site_tmplvar_contentvalues
WHERE modx_site_tmplvar_contentvalues.tmplvarid = 324
AND modx_site_tmplvar_contentvalues.contentid = content.id
) AS featured,
date_values.value as date
FROM modx_site_content AS content
LEFT JOIN
modx_site_tmplvar_contentvalues AS tv_values
ON tv_values.contentid = content.id
LEFT JOIN
modx_site_tmplvar_contentvalues AS date_values
ON date_values.contentid = content.id
WHERE content.parent = 1842
AND content.published = 1
AND date_values.tmplvarid = 289
AND date_values.value >= CURDATE()
GROUP BY tv_values.contentid
ORDER BY featured DESC, date ASC

Count tweets between dates (mysql)

I have an assignment to create a twitter like database. And in this assignment i have to filter out the trending topics. My idea was to count the tweets with a specific tag between the date the tweet was made and 7 days later, and order them by the count.
I have the following 2 tables i am using for this query :
Table Tweet : id , message, users_id, date
Table Tweet_tags : id, tag, tweet_id
Since mysql isn't my strong point at all im having trouble getting any results from the query.
The query i tried is :
Select
Count(twitter.tweet_tags.id) As NumberofTweets,
twitter.tweet_tags.tag
From twitter.tweet
Inner Join twitter.tweet_tags On twitter.tweet_tags.tweet_id = twitter.tweet.id
WHERE twitter.tweet_tags.tag between twitter.tweet.date and ADDDATE(twitter.tweet.date, INTERVAL 7 day)
ORDER BY NumberofTweets
The query works, but gives no results. I just can't get it to work. Could you guys please help me out on this, or if you have a better way to get the trending topics please let me know!
Thanks alot!
This is equivalent to your query, with table aliases to make it easier to read, with BETWEEN replaced by two inequality predicates, and the ADDDATE function replaced with equivalent operation...
SELECT COUNT(s.id) As NumberofTweets
, s.tag
FROM twitter.tweet t
JOIN twitter.tweet_tags s
ON s.tweet_id = t.id
WHERE s.tag >= t.date
AND s.tag <= t.date + INTERVAL 7 DAY
ORDER
BY NumberofTweets
Two things pop out at me here...
First, there is no GROUP BY. To get a count by "tag", you want at GROUP BY tag.
Second, you are comparing "tag" to "date". I don't know your tables, but that just doesn't look right. (I expect "date" is a DATETIME or TIMESTAMP, and "tag" is a character string (maybe what my daughter calls a "hash tag". Or is that tumblr she's talking about?)
If I understand your requirement:
For each tweet, and for each tag associated with that tweet, you want to get a count of the number of other tweets, that have a matching tag, that are made within 7 days after the datetime of the tweet.
One way to get this result would be to use a correlated subquery. (This is probably the easiest approach to understand, but is probably not the best approach from a performance standpoint).
SELECT t.id
, s.tag
, ( SELECT COUNT(1)
FROM twitter.tweet_tags r
JOIN twitter.tweet q
ON q.id = r.tweet_id
WHERE r.tag = s.tag
AND q.date >= t.date
AND q.date <= t.date + INTERVAL 7 DAY
) AS cnt
FROM twitter.tweet t
JOIN twitter.tweet_tags s
ON s.tweet_id = t.id
ORDER
BY cnt DESC
Another approach would be to use a join operation:
SELECT t.id
, s.tag
, COUNT(q.id) AS cnt
FROM twitter.tweet t
JOIN twitter.tweet_tags s
ON s.tweet_id = t.id
LEFT
JOIN twitter.tweet_tags r
ON r.tag = s.tag
LEFT
JOIN twitter.tweet q
ON q.id = r.tweet_id
AND q.date >= t.date
AND q.date <= t.date + INTERVAL 7 DAY
GROUP
BY t.id
, s.tag
ORDER
BY cnt DESC
The counts from both of these queries assume that tweet_tags (tweet_id, tag) is unique. If there are any "duplicates", then including the DISTINCT keyword, i.e. COUNT(DISTINCT q.id) (in place of COUNT(1) and COUNT(q.id) respectively) would get you the count of "related" tweets.
NOTE: the counts returned will include the original tweet itself.
NOTE: removing the LEFT keywords from the query above should return an equivalent result, since the tweet/tag (from t/s) is guaranteed to match itself (from r/q), as long as the tag is not null and the tweet date is not null.
Those queries are going to have problematic performance on large sets. Appropriate covering indexes are going to be needed for acceptable performance:
... ON twitter.tweet_tags (tag, tweet_id)
... ON twitter.tweet (date)