Mysql Distinct gives a syntax error - remove duplicate results - mysql

With some help from someone here on Stackoverflow i almost got to the point where the query is perfect. I need some little help adjusting the query to fit my needs, and i hope someone could help me out again.
Here's the case, i have the following 2 tables :
Tweet
and
Tweet_tags
Every tweet has a tag, and a tweet can have more of the same tags. I want to count how many tweets have the same tag within a time span of week since the tweet was posted.
Here is the query as it is now :
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
The results of this query are :
| ID | Tag | Cnt |
-------------------------------
| 1 | Testtag | 2 |
| 2 | Testtag | 1 |
| 3 | tweettag3 | 1 |
| 4 | tweettag2 | 1 |
I have the testtag 2 times in my database, so the first result is correct, tweettag3 and tweettag2 are in my database 1 time so thats good as well, but these will probably also show multiple results when i add them again. I tried using DISTINCT on s.tag to get rid of the duplicate result, however this gives me a syntax error.
So how i want it to be is :
| ID | Tag | Cnt |
-------------------------------
| 1 | Testtag | 2 |
| 2 | tweettag3 | 1 |
| 3 | tweettag2 | 1 |
Could someone please help me out on this? If you need more information please say so!
Thanks !!
Edit :
This is how the tables look:
Tweet
---------------
ID
Message
users_id
Date
Tweet_tags
---------------
id
tag
tweet_id

You can just use select distinct, so the query would be:
SELECT distinct 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
Just remove the id field from the select.
If you want an id, then you can use the MySQL feature of hidden columns to have:
select t.id, s.tag, . . .
group by tag
order by cnt desc

Try this query
SELECT
t.id ,
s.tag ,
s.count
FROM twitter.tweet t
JOIN (
SELECT
COUNT(id) as count,
tweet_id,
tag
FROM tweet_tags
GROUP BY tweet_id
) as s ON s.tweet_id = t.id
AND t.tag = s.tag
ORDER BY cnt DESC

Related

How to get most recent record with a group by

How can I get the latest product title when I'm doing a group by category?
For example, assume this is my table of products:
id | title | catID | timestamp
1 | apple | 1 | 2020-07-13 08:21:47
2 | pear | 1 | 2020-07-14 08:21:47
3 | kiwi | 1 | 2020-07-15 08:21:47
I want a query to give me the total products and the last added. It's a little greedy of a query, and makes it a little more difficult, but I thought I could do this with a group by. If possible, I'd really prefer to keep this all one query. I'm worried I'll need to add a sub query though.
SELECT
p.catID
, COUNT(p.id) as items
, MAX(p.id) as last_added_id
, CASE WHEN p.id = MAX(p.id) THEN p.title
FROM products p
WHERE p.catID = 1
GROUP BY p.catID
The problem is, I'm getting a null for the title.
I actually can't think of a way to do this without using a subquery. Assuming you are using MySQL 8+, we can use window functions here:
SELECT id, title, catID, timestamp, cnt
FROM
(
SELECT *, COUNT(*) OVER () AS cnt
FROM products
WHERE catID = 1
) t
ORDER BY timestamp DESC
LIMIT 1;
With window functions COUNT(), MAX() and FIRST_VALUE():
select distinct
p.catid,
count(*) over (partition by p.catid) items,
first_value(p.id) over (partition by p.catid order by p.timestamp desc) id,
first_value(p.title) over (partition by p.catid order by p.timestamp desc) title,
max(p.timestamp) over (partition by p.catid) timestamp
from products p
See the demo.
Results:
> catid | items | id | title | timestamp
> ----: | ----: | -: | :---- | :------------------
> 1 | 3 | 3 | kiwi | 2020-07-15 08:21:47
I dont know if this what you mean.
SELECT
p.catID
COUNT(p.id) as items
MAX(p.id) as last_added_id
sum( if( p.id = 1, p., 0 ) ) AS Total,
(SELECT max(title) FROM product WHERE catID= 1)
FROM products p
WHERE p.catID = 1
GROUP BY p.catID

Get Previous and Next record from database and loop them

I have a table with IDs from 1 to 8. I want something like this
If i'm on 4, I should get 3,5
If i'm in on 1, I should get 8,2
If in on 8, I should get 7, 1
Basically looping through the table records
This is my current code
-- previous or last, if there is no previous
SELECT *
FROM news
WHERE id < 1 OR id = MAX(id)
ORDER BY id DESC
LIMIT 1
-- next or first, if there is no next
SELECT *
FROM news
WHERE id > 1 OR id = MIN(id)
ORDER BY id ASC
LIMIT 1
But it says Invalid use of group function. Any help?
If id is sequential you can do this:
SQL DEMO
SELECT o.id,
COALESCE(b.id, (SELECT MAX(ID) FROM Table1)) as before_id,
COALESCE(a.id, (SELECT MIN(ID) FROM Table1)) as after_id
FROM Table1 o
LEFT JOIN Table1 b
ON o.id = b.id + 1
LEFT JOIN Table1 a
ON o.id = a.id - 1
ORDER BY o.id
OUTPUT
| id | before_id | after_id |
|----|-----------|----------|
| 1 | 8 | 2 |
| 2 | 1 | 3 |
| 3 | 2 | 4 |
| 4 | 3 | 5 |
| 5 | 4 | 6 |
| 6 | 5 | 7 |
| 7 | 6 | 8 |
| 8 | 7 | 1 |
If ids are not sequential you need use row_number() (mysql ver 8+) or session variables to create a sequence.
I guess that you want to show "prev" and "next" buttons, when the user views a news article. I would get the previous and the next ID in the main query, when you fetch the article data:
select n.*, -- select columns you need
coalesce(
(select max(n1.id) from news n1 where n1.id < n.id ),
(select max(id) from news)
) as prev_id,
coalesce(
(select min(n1.id) from news n1 where n1.id > n.id ),
(select min(id) from news)
) as next_id
from news n
where n.id = ?
db-fiddle demo
Now you can use prev_id and next_id for your buttons, or prefetch the corresponding articles with a simple select * from news where id = ? query.
You can remove the filtering in your approach and add logic to the ORDER BY:
(SELECT n.*
FROM news
ORDER BY (id < 1), id DESC
LIMIT 1
) UNION ALL
(SELECT n.*
FROM news
ORDER BY (id > 1), id ASC
LIMIT 1
) ;
If you want the id values in one row, you can use aggregation:
select coalesce(max(case when id < 1 then id end), max(id)) as prev_id,
coalesce(min(case when id > 1 then id end), min(id)) as next_id
from news n;
In both cases, 1 is a sample id and the "1" can be replaced with any value.

MySQL query GROUP BY two columns

I need help with MySQL query
I have this cenary:
table A
- page_id
- rev_id
- date
one page_id can have multiples rev_id
table B
- rev_id
- words
I have what words have in each revision
I need return for each date the quantity of words that I have in the
last rev_id in each page_id
Example:
table A
page_id | rev_id | date
---------------------------------
1 | 231 | 2002-01-01
2 | 345 | 2002-10-12
1 | 324 | 2002-10-13
3 | 348 | 2003-01-01
--
table B
rev_id | words
---------------
231 | 'ask'
231 | 'the'
231 | 'if'
345 | 'ask'
324 | 'ask'
324 | 'if'
348 | 'who'
magical sql here edited to show how its calculated {page_id : [words]}
date | count(words)
--------------------------
2002-01-01 | 3 { 1:[ask, the, if] }
2002-10-12 | 4 { 1:[ask, the, if], 2:[ask] }
2002-10-13 | 3 { 1:[ask, if], 2:[ask] }
2003-01-01 | 4 { 1:[ask, if], 2:[ask], 3:[who] }
I did this query, but my date are fixed and I need for all dates contained in table revision:
SELECT SUM(q)
FROM (
SELECT COUNT(equation) q
FROM revision r, equation e
WHERE r.rev_id in (
SELECT max(rev_id)
FROM revision
WHERE date < '2006-01-01'
GROUP BY page_id
)
AND r.rev_id = e.rev_id
GROUP BY date
) q;
Solved
My friend help-me to create query to solve my problem!
select s.date, count(words) from
(select d.date, r.page_id, max(r.rev_id) as rev_id
from revision r, (select distinct(date) from revision) d
where d.date >= r.date group by d.date, r.page_id) s
join words e on e.rev_id = s.rev_id
group by s.date;
I think this is a basic join and group by:
select a.date, count(*)
from a join
b
on a.rev_id = b.rev_id
group by a.date;
EDIT:
Oh, I think I get it. This is a cumulative thing. That makes it more complicated.
select d.date,
(select count(*)
from a join
b
on a.rev_id = b.rev_id
where a.date <= d.date and
a.rev_id = (select max(a2.rev_id) from a a2 where a2.date = a.date and a2.date <= d.date)
) as cnt
from (select date from a) d;
But that won't work in MySQL because of the nesting of the correlation clause. So, we can restructure the logic as:
select a.date, count(*)
from (select a.*,
(select max(a2.rev_id)
from a a2
where a2.date <= a.date and a2.page_id = a.page_id
) as last_rev_id
from a
) a join
b
on a.last_rev_id = b.rev_id
group by a.date;

mysql select distinct but latest row

How can I select the latest 'score' from a table for each distinct 'site' that exists?
For example:
site | date | score
a | 20140101 | 10
a | 20140102 | 8
b | 20140103 | 11
b | 20140202 | 9
I'd like to return one result for a and b, but only their latest entry (by date).
a | 20140102 | 8
b | 20140202 | 9
I know how to group by site, or select distinct site, but not sure how to narrow it down to their latest dates.
edit: this should be dynamic for thousands of distinct sites.
You can do it with a union
(SELECT
site,date,score
FROM
`table`
WHERE
site = 'a'
ORDER BY date DESC
LIMIT 0,1)
UNION
(SELECT
site,date,score
FROM
`table`
WHERE
site = 'b'
ORDER BY date DESC
LIMIT 0,1)
A little more convoluted if you don't want to write a UNION
SELECT
t.site, t.date, t.score
FROM
`table` t
JOIN (
SELECT ti.site,MAX(ti.date) AS dt
FROM `table` ti
GROUP BY ti.site
) t2 ON t2.site = t.site and t2.dt = t.date

Unknown column when subquery refer to outer table used at FROM clause

I have this table:
person table
| id | name |
| 1 | person1 |
| 2 | person2 |
person_grade table
| id | person_id | grade | grade_date |
| 1 | 1 | grade1 | 2010-01-01 |
| 2 | 1 | grade2 | 2012-01-01 |
| 3 | 2 | grade3 | 2010-05-05 |
| 4 | 2 | grade4 | 2012-03-03 |
I want to know person grade at a specific time, say 2012-02-02. How to achieve this?
The closest I got was, with this query:
SELECT t1.id, t1.name,
(SELECT grade FROM (
(SELECT s1.grade, s1.grade_date FROM person_grade AS s1
WHERE s1.grade_date >= '2012-02-01' AND s1.person_id = t1.id
ORDER BY s1.grade_date LIMIT 1)
UNION ALL
(SELECT s1.grade, s1.grade_date FROM person_grade AS s1
WHERE s1.grade_date <= '2012-02-01' AND s1.person_id = t1.id
ORDER BY s1.grade_date DESC LIMIT 1)
) AS ss ORDER BY grade_date LIMIT 1) AS grade_person
FROM person AS t1
But at MySQL that give me an error
"Unknown column 't1.id' in 'where clause'".
Please advise.
TIA
SELECT name,grade FROM person p
INNER JOIN person_grade pg
ON p.id=pg.person_id
WHERE pg.grade_date='2012-02-02'
I dont know about the syntax about the MySql but u can do something like this
SELECT GRADE FROM person_grade WHERE DATE(GRADE_DATE,YEAR) = DATE(SEARCH_DATE,YEAR) AND DATE(GRADE_DATE,MONTH) = DATE(SEARCH_DATE,MONTH) AND DATE(GRADE_DATE,DAY) = DATE(SEARCH_DATE,DAY)
SELECT p.name
, pg.grade
FROM person p
INNER JOIN person_grade pg ON p.id = pg.person_id
WHERE DATE(pg.grade_date) = '2012-02-02'
If this works where #mhasan's answer did not, then it's most likely because of the data type of the grade_date table. If it's DATETIME, then it stores the time element of the date and that makes searching on that field bothersome.
If you don't need the time element, try changing the data type of the column to DATE. It should use less space and make searching easier.
EDIT: Wait, I just read that question again... you don't want records where the date MATCHES the query, you want something slightly trickier. Let me think on this one...
Thanks for the replay everyone, I think I found the solution, move subquery to WHERE clause and use MIN function at date diff. Here is the query :
SELECT p.id, p.name, pg.grade, pg.grade_date
FROM person AS p
LEFT JOIN person_grade AS pg ON p.id = pg.person_id
WHERE DATEDIFF ( '2012-02-02', pg.grade_date ) =
( SELECT MIN ( DATEDIFF ( '2012-02-02', spg.grade_date ) )
FROM person AS sp
LEFT JOIN person_grade AS spg ON sp.id = spg.person_id
WHERE DATEDIFF ( '2012-02-02', spg.grade_date ) > 0 AND sp.id = p.id )
Result:
| id | name | grade | grade_date |
| 1 | person1 | grade2 | 2012-01-01 |
| 2 | person2 | grade3 | 2010-05-05 |
Seems reference to outer table doesn't work under FROM clause, but work elsewhere (at least at MySQL).
Thanks for the hint from other question: Retrieve maximal / minimal record.