I have the following schema:
And I want to generate a table with 3 columns - tag's name, thread's name, answers count.
Example:
Foo bar 5
Foo something 6
Foo2 somethingElse 3
What's more, there's a table "priority_threads" (1:1 with threads). I want to display only those priority threads in this table.
How can I do this? I have absolutely no idea how to even start. The only thing I did is:
SELECT tag.name, thread.title, COUNT(answer.id_answer)
FROM tag, thread, answer
WHERE thread.id_tag = tag.id_tag
AND answer.id_thread = thread.id_thread
AND thread.id_thread = priority_threads.id_thread
GROUP BY tag.name, thread.title
ORDER BY tag.name;
However using this query all values in the answer's count column are the same - count(*) from Answer table...
Try this:
SELECT tag.name, thread.title, COUNT(answer.id_answer)
FROM tag
JOIN thread ON
tag.id_tag=thread.id_tag
JOIN answer ON
thread.id_thread = answer.id_thread
JOIN priority_threads
ON thread.id_thread = priority_threads.id_thread
GROUP BY tag.name, thread.title
ORDER BY tag.name;
Here is a SQLfiddle link to the query to test it out;
Sample code:
CREATE TABLE Tag
(
id_tag int auto_increment primary key,
name varchar(20)
);
INSERT INTO Tag
(name)
VALUES
('Foo'),
('Foo2');
CREATE TABLE Thread
(
id_thread int auto_increment primary key,
id_tag int,
title varchar(20)
);
INSERT INTO Thread
(id_tag, title)
VALUES
(1,'Bar'),
(1,'Something'),
(2,'SomethingElse');
CREATE TABLE Answer
(
id_answer int auto_increment primary key,
id_thread int,
text varchar(200)
);
INSERT INTO Answer
(id_thread, text)
VALUES
(1,'jlkjalkjl'),
(1,'ioioixhakjjkj'),
(1, 'jjalkjijkajk'),
(1, 'jjalkjijkajk'),
(1, 'jjalkjijkajk'),
(2, 'jjalkjijkajk'),
(2, 'jjalkjijkajk'),
(2, 'jjalkjijkajk'),
(2, 'qqweeweraata'),
(2, 'jjalkjijkajk'),
(2, 'jjalkjijkajk'),
(3, 'popoapopop'),
(3, 'zkkasjkljz'),
(3, 'jjalkjijkajk')
;
CREATE TABLE priority_threads
(
id_priority_threads int auto_increment primary key,
id_thread int,
priority int
);
INSERT INTO priority_threads
(id_thread, priority)
VALUES
(1,1),
(3,2);
so:
SELECT tag.name, thread.title, COUNT(answer.id_answer)
FROM tag, thread, answer
WHERE thread.id_tag = tag.id_tag
AND answer.id_thread = thread.id_thread
GROUP BY tag.name, thread.title
ORDER BY tag.name;
can look like this:
SELECT tag.name, thread.title, COUNT(answer.id_answer)
FROM tag
JOIN thread ON
tag.id_tag=thread.id_tag
JOIN answer ON
thread.id_thread = answer.id_thread
GROUP BY tag.name, thread.title
ORDER BY tag.name;
The results are the same, the former is preferred by most. Okay, but I think the real issue here is this line here in your posted code:
AND thread.id_thread = priority_threads.id_thread
you are referencing another table, priority_threads, which is not in your query. Either add the join or remove it and you should be good to go.
-james
Related
I want to know if the data exist in set datatype by using an array.
For example:
The table is
id role
1 'a'
2 'b'
3 'a','b','c'
$role = ['a']
SQL = SELECT * FROM ... WHERE $role in role (have 'a')
And the result is
id role
1 'a'
3 'a','b','c'
Or
$role = ['a','b']
SQL = SELECT * FROM ... WHERE $role in role (have 'a' and 'b')
And the result is
id role
3 'a','b','c'
I tried to use FIND_IN_SET(('a','b'),role)>0; but it's not work. I need to use a loop.
Have any method better than this?
MySQL doesn't have an array type. What you are using is a string, that happens to contain commas.
The better method in a relational database is to store multi-valued attributes in a second table, with one value per row.
CREATE TABLE role (
entity_id INT NOT NULL,
role CHAR(1) NOT NULL,
PRIMARY KEY(entity_id, role),
FOREIGN KEY (entity_id) REFERENCES mytable (id)
);
INSERT INTO role VALUES
(1, 'a'), (2, 'b'), (3, 'a'), (3, 'b'), (3, 'c');
If you need to find an entry that has both role 'a' and role 'b', this is called relational-division. There are several solutions when using a normalized table. Here's one:
SELECT mytable.*
FROM mytable
JOIN role AS r1 ON mytable.id = r1.entity_id
JOIN role AS r2 ON mytable.id = r2.entity_id
WHERE r1.role = 'a' AND r2.role = 'b';
Thank #Bill Karwin for a trick.
Now I am using:
CREATE TABLE `test`.`user_role` (
`userId` INT NOT NULL,
`a` BOOLEAN NOT NULL,
`b` BOOLEAN NOT NULL,
PRIMARY KEY (`userId`)
) ENGINE = InnoDB;
and view table that connects the user table and role table.
Let's assume I've got a database with two tables: people which contains person's id and his/her birth year for each person and parents which contains the (parent_id, child_id) pairs to represent the relative relationships between people. To make the explanation easier let's assume each person has either 0 children or 1 child. Here is an example of the data in the database (as a set of SQL statements to create it on MySQL):
CREATE TABLE people (
id INTEGER NOT NULL AUTO_INCREMENT,
birth_year INTEGER NOT NULL,
CONSTRAINT PRIMARY KEY(id)
);
CREATE TABLE parents (
parent_id INTEGER NOT NULL,
child_id INTEGER NOT NULL,
CONSTRAINT PRIMARY KEY(parent_id,child_id)
);
-- Not creating FOREIGN KEYS, because it's just an example
INSERT INTO people (id, birth_year) VALUES (1, 1937);
INSERT INTO people (id, birth_year) VALUES (2, 1943);
INSERT INTO people (id, birth_year) VALUES (3, 1974);
INSERT INTO people (id, birth_year) VALUES (4, 2001);
INSERT INTO people (id, birth_year) VALUES (5, 2020);
INSERT INTO parents (parent_id, child_id) VALUES (1, 4);
INSERT INTO parents (parent_id, child_id) VALUES (3, 5);
Result:
Now I want to make up a query which will retrieve the id of the person, whose child was born at the earliest age of the parent (for example, if I was born in 1234 and my child was born in 1300, then my age when my child was born was 1300 - 1234 = 66 and I would like to find a person which got their child earlier than others).
I have made up some queries for it, but each of them either didn't work or had duplications or both. The one I like most is
SELECT id AS pid, -- Parent id
(SELECT child_id FROM parents WHERE parent_id=pid) AS cid -- Child id
FROM people WHERE
EXISTS(SELECT cid) -- Only selecting parents who have children (not sure this part is correct)
ORDER BY (birth_year - (SELECT birth_year FROM people WHERE id=cid)) ASC -- Order by age when they got their child
LIMIT 1;
But this one fails in MySQL with the error:
ERROR 1054 (42S22) at line 24: Unknown column 'cid' in 'field list'
How do I fix the error? Another thing I am worried about is that as a result, I will select not only the parent's id but also the id of one of his/her children. Is it possible to avoid it?
Probably there is a better way to select the data I'm looking for?
You can get the ages by using joins:
select c.*, (p.birth_year - c.birth_year) as parent_age
from parents pa join
people p
on pa.parent_id = p.id join
people c
on pa.child_id = pc.id;
To get all the rows with the minimum, use window functions:
select x.*
from (select c.*, (p.birth_year - c.birth_year) as parent_age,
min(p.birth_year - c.birth_year) over () as min_age
from parents pa join
people p
on pa.parent_id = p.id join
people c
on pa.child_id = pc.id
) x
where parent_age = min_age;
I have two tables with below constructions:
create table article (
id int NOT NULL AUTO_INCREMENT PRIMARY KEY,
title varchar(50) NOT NULL,
text text,
date timestamp NOT NULL default CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1;
create table article_relate (
article_id_1 int NOT NULL,
article_id_2 int NOT NULL
);
Example records in table "article"
id title
--------------------------------------
1 About Ferrari
2 About Lamborghini
3 About my friends
4 About my kitchen
In the second table - "article_relates", I described the relations of similar themed records from the first table.
article_id_1 article_id_2
-----------------------------------
1 2
2 1
when I add the next article, I will link it to the first article I know about identical subject matter.
Insert into article (id, title) values (5, 'About Maserati');
and a link to a similar article with id = 2
Insert into article_relate (article_id_1, article_id_2) values (5, 2);
Insert into article_relate (article_id_1, article_id_2) values (2, 5);
Now I want to send a query to the database to get all entries related to the article about id = 5. Also, those entries with which the article about id = 2 is associated. The result for this query should be records with id = 5, 2 and 1
What query should be built to get this effect?
Can you help me?
You can simply use a select with an inner select query.
SELECT article.id,article.name FROM article WHERE article.id IN (SELECT article_id_2 from article_relate where article_id_1 = 5);
sounds like a JOIN clause:
SELECT article.id,
article.name,
article_relate.id AS article_relate_id
FROM article
LEFT JOIN article_relates ON article.id = article_relate.article_id_1
OR article.id = article_related
WHERE article.id = 5
will list all related articles for article id 5. does this help you?
about join in mysql - https://dev.mysql.com/doc/refman/8.0/en/join.html
SQL Fiddle And Here is my whole code:
DROP TABLE tmdb_movies;
CREATE TABLE tmdb_movies (
tmdb_id INTEGER NOT NULL PRIMARY KEY,
movie_title TEXT NOT NULL,
popularity INTEGER NOT NULL
);
INSERT INTO tmdb_movies (tmdb_id, movie_title, popularity) VALUES
(1, 'Logan', '88.4'),
(2, 'Iron Man', '74.3'),
(3, 'SuperMan', '102.56');
DROP TABLE genres;
CREATE TABLE genres (
tmdb_id INTEGER NOT NULL,
genres_name TEXT NOT NULL
);
INSERT INTO genres (tmdb_id, genres_name) VALUES
(1, 'Crime'),
(1, 'Comedy'),
(1, 'Drama'),
(2, 'Action'),
(2, 'Horror'),
(2, 'Documentary'),
(3, 'Music');
SELECT distinct genres.tmdb_id
FROM genres
JOIN tmdb_movies USING (tmdb_id)
ORDER BY tmdb_movies.popularity DESC
What, i am doing here is: I want to sort table by popularity column in tmdb_movies table.
The SQL query is working perfectly fine in the [SQL Fiddle][1]
But, i am getting this error, when i run it in my MySQL
3065 - Expression #1 of ORDER BY clause is not in SELECT list, references column 'DB.tmdb_movies.popularity' which is not in SELECT list; this is incompatible with DISTINCT
This is probably because of my default SQL mode. Do i need to change the mode? Anyway to do this, without changing the mode, because i think default mode will be better?
Is there a specific reason you don't want to add the popularity to the select?
SELECT distinct genres.tmdb_id, tmdb_movies.popularity
FROM genres
JOIN tmdb_movies USING (tmdb_id)
ORDER BY tmdb_movies.popularity DESC
i have USERS TABLE that contains the following fields _id name number_of_posts
and i have POSTS TABLE that contains the following fields _id user_id post_text
no the relationship between the users and posts is one- to -many i.e one user have many post
the question is how to update the number_of_posts in users table such that it will hold the number of post in the posts table
UPDATE
I have these tables filled with about hundreds of records there is no way to use the trigger to update the number_of_posts .
Below given sample script shows how to get the number of posts associated with a user by joining both users and posts table using LEFT OUTER JOIN and then uses GROUP BY on the *_id* field in users table to fetch the posts count.
Click here to view the sample in SQL Fiddle.
Query:
CREATE TABLE users
(
_id INT NOT NULL
, name VARCHAR(30) NOT NULL
);
CREATE TABLE posts
(
_id INT NOT NULL
, _userid INT NOT NULL
, name VARCHAR(30) NOT NULL
);
INSERT INTO users (_id, name) VALUES
(1, 'user 1'),
(2, 'user 2'),
(3, 'user 3');
INSERT INTO posts (_id, _userid, name) VALUES
(1, 1, 'post 1'),
(2, 1, 'post 2'),
(3, 2, 'post 3');
SELECT u._id
, u.name
, COUNT(p._id) number_of_posts
FROM users u
LEFT OUTER JOIN posts p
ON u._id = p._userid
GROUP BY u._id;
Output:
_ID NAME NUMBER_OF_POSTS
--- ------ ---------------
1 user 1 2
2 user 2 1
3 user 3 0
You can do this with a trigger on the posts table. Every time a post is inserted, update the number_of_posts on the users table.
See http://dev.mysql.com/doc/refman/5.6/en/triggers.html
Update: full worked solution
drop table if exists users;
create table users (
_id bigint unsigned auto_increment primary key,
name varchar(50) not null,
number_of_posts integer not null default 0
);
drop table if exists posts;
create table posts (
_id bigint unsigned auto_increment primary key,
user_id bigint unsigned not null,
post_text text
);
-- Populate with dummy data
insert into users (name) values ('Bob'), ('Sally');
insert into posts (user_id, post_text)
values (1, 'First!!'), (1, 'Second...'),
(2, 'Post 1'), (2, 'another post'), (2, 'more posts');
-- One-time update of users table
update users u
set u.number_of_posts = (
select count(0) from posts p
where u._id = p.user_id
);
-- trigger to keep post count up to date when future posts are made
drop trigger if exists trg_post_count;
delimiter $$
create trigger trg_post_count
after insert on posts
for each row
begin
select count(0) into #post_count
from posts
where user_id = NEW.user_id;
update users
set number_of_posts = #post_count
where _id = NEW.user_id;
end;
$$
delimiter ;
-- test trigger
insert into posts (user_id) values (2);
select * from users;
As a one time update, try this, but the trigger solution is the right one to keep it current:
UPDATE users AS u SET u.number_of posts = (SELECT COUNT(*) FROM posts AS p WHERE p.user_id=u.user_id)
As dwurf suggested, I would definitely recommend putting this in a trigger to keep the field up-to-date. However, I completely disagree with him that it should simply be a number that you increment on each update. That's fragile and error prone. You should use the following query to figure out exactly how many posts each user has:
update
users
set
number_of_posts = (
select
count(0)
from
posts p
where
p.user_id = users.id
)
;