Mysql group joins same id together but still get duplicates [duplicate] - mysql

This question already has answers here:
Selecting from two tables with inner join and limit [duplicate]
(5 answers)
Closed last year.
I would like to group posts with same id. Now i get duplicate of posts based on how many comments. Which is wrong, it should group and show the latest comment and not create row for each comment
Table structure:
CREATE TABLE users(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255),
myKey VARCHAR(100)
);
INSERT INTO users (name, myKey)
VALUES ("Gregor", "kx4ht"),
("Liza", "1lPxk"),
("Matt", "mP3fd"),
("Bob", "zStr5");
CREATE TABLE comments(
id INT PRIMARY KEY AUTO_INCREMENT,
post_id INT,
comment VARCHAR(255)
);
INSERT INTO comments (post_id, comment)
VALUES (1, "Hello world"),
(2, "I like iceCream"),
(1, "I like unicorns");
CREATE TABLE post(
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
text VARCHAR(255),
last_comment INT,
FOREIGN KEY (`last_comment`) REFERENCES `comments` (`id`)
);
INSERT INTO post (user_id, text, last_comment)
VALUES (1, "My first post", 3),
(1, "What is this?", 2);
Query:
SELECT u.name, p.text, c.comment
FROM post p
INNER JOIN users u ON (u.id = p.user_id)
INNER JOIN comments c ON (c.post_id = p.id)
WHERE (p.user_id = 1)
GROUP BY p.id, c.id;
Now
name text comment
Gregor My first post Hello world
Gregor What is this? I like iceCream
Gregor My first post I like unicorns
What i expect:
name text comment
Gregor What is this? I like iceCream
Gregor My first post I like unicorns

This should solve your issue
SELECT u.name, p.text, c.comment
FROM post p
INNER JOIN users u ON (u.id = p.user_id)
INNER JOIN
(
SELECT *
FROM comments c1
WHERE NOT EXISTS (
SELECT *
FROM comments c2
WHERE c1.post_id = c2.post_id AND
c1.id < c2.id
)
) c ON (c.post_id = p.id)
WHERE (p.user_id = 1)
GROUP BY p.id, c.id;

Related

How is the best way to perform this search in my mysql database? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I have 3 tables and its respectives structure:
users: id(1),name
colors: id(2),name,tags
hobbies: id(3),name,tags
userSettings: id,int(1) userid,int(2) color,int(3) hobbie
Tables values:
users: {1,john},{2,ana}
colors: {1,blue,blue azul celeste },{2,red,red vermelho rosso}
hobbies: {1,sing,cantare cantar sing},{2,run,running correr runner}
userSettings: {1,1,1,2},{2,2,2,1}
So... what I need is to find which users has the color or hobbie searched by the user. For example: if I search by "blue" or "runner" I have to find user 1 - John.
If I search by "rosso" or "sing" I have to find user 2 - Ana. I know how to do this with 3 queries but I know that exists a method to improve this to just one query that would be fast since the fields are indexed.
Does any one can help?
Add a fulltext index to the tables you want to search, then join the tables in the query.
https://www.db-fiddle.com/f/peZLQ6PVLMRCytcP9EUp3f/12
Schema:
CREATE TABLE users (
id int NOT NULL AUTO_INCREMENT,
name varchar(255),
PRIMARY KEY (id)
);
INSERT INTO users (name) VALUES ('john'), ('ana');
CREATE TABLE colors (
id int NOT NULL AUTO_INCREMENT,
name varchar(255),
tags varchar(255),
PRIMARY KEY (id),
FULLTEXT (tags)
);
INSERT INTO colors (name, tags)
VALUES
('blue','blue azul celeste'),
('red','red vermelho rosso');
CREATE TABLE hobbies (
id int NOT NULL AUTO_INCREMENT,
name varchar(255),
tags varchar(255),
PRIMARY KEY (id),
FULLTEXT (tags)
);
INSERT INTO hobbies (name, tags)
VALUES
('sing','cantare cantar sing'),
('run','running correr runner');
CREATE TABLE userSettings (
id int NOT NULL AUTO_INCREMENT,
userid int,
color int,
hobbie int,
PRIMARY KEY (id)
);
INSERT INTO userSettings (userid, color, hobbie)
VALUES
(1,1,2),
(2,2,1);
Query:
SELECT users.* FROM users
LEFT JOIN userSettings ON userSettings.userid = users.id
LEFT JOIN colors ON colors.id = userSettings.color
LEFT JOIN hobbies ON hobbies.id = userSettings.hobbie
WHERE
MATCH(colors.tags) AGAINST ('runner' IN BOOLEAN MODE)
OR MATCH(hobbies.tags) AGAINST ('runner' IN BOOLEAN MODE)
;
Or you could create a view table. EG:
https://www.db-fiddle.com/f/8kojtBefpySA4ckDqM747H/2
Schema
//As above +
CREATE VIEW searchview AS (SELECT userid, users.name as name, concat_ws(' ',colors.tags,hobbies.tags) as tags FROM users
LEFT JOIN userSettings ON userSettings.userid = users.id
LEFT JOIN colors ON colors.id = userSettings.color
LEFT JOIN hobbies ON hobbies.id = userSettings.hobbie);
Query
SELECT * FROM searchview
WHERE tags REGEXP '[[:<:]]runner[[:>:]]';
It's really going to depend on how you're implementing your search (I assume you're using PHP?)
In general something like this could work, if you replace the word by the variable in your search form:
Schema (MySQL v5.7)
create table users (id int, name varchar(10));
create table colors (id int, name varchar(10), tags varchar(50));
create table hobbies (id int, name varchar(10), tags varchar(50));
create table userSettings (id int, user_id int, color_id int, hobbie_id int);
insert into users values (1, 'John');
insert into users values (2, 'Ana');
insert into colors values (1,'blue','blue azul celeste');
insert into colors values (2,'red','red vermelho rosso');
insert into hobbies values (1,'sing','cantare cantar sing');
insert into hobbies values (2,'run','running correr runner');
insert into userSettings values (1,1,1,2);
insert into userSettings values (2,2,2,1);
Query #1
select u.* from users u, colors c, hobbies h, userSettings us
where
us.user_id = u.id and
us.color_id = c.id and
us.hobbie_id = h.id and
(c.tags like '%blue%' or
h.tags like '%blue%' or
c.name like '%blue%' or
h.name like '%blue%'
);
| id | name |
| --- | ---- |
| 1 | John |
View on DB Fiddle
The following query should get the job done as expected. The logic is to LEFT JOIN the users table with the colors and hobbies table, using userSettings as a bridge. The search parameter is checked in the join conditions. Then, the WHERE clause ensures that at least one hobby or color matches the search parameters :
SELECT u.*
FROM
users u
LEFT JOIN userSettings us ON us.user_id = u.id
LEFT JOIN colors c
ON c.id = us.color_id
AND ( c.name = #search OR c.tags LIKE CONCAT('%', #search, '%') )
LEFT JOIN hobbies h
ON h.id = us.hobbie_id
AND (h.name = #search OR h.tags LIKE CONCAT('%', #search, '%') )
WHERE
c.id IS NOT NULL OR h.id IS NOT NULL
You can see it action with your sample data in this demo on DB Fiddle. For example, when searching for 'sing', this returns :
| id | name |
| --- | ---- |
| 2 | Ana |
Try this:
SELECT DISTINCT u.id || ' - ' || u.name as USER
FROM userSettings us
JOIN users u
ON u.id = us.userid
JOIN colors c
ON c.id = us.color
JOIN hobbies h
ON h.id = us.hobbie
WHERE
c.name LIKE '%blue%'
OR c.tags LIKE '%blue%'
OR h.name LIKE '%runner%'
OR h.tags LIKE '%runner%';
Input:
Search for blue or runner
Output:
USER
1 - John

sql - A query to retrieve information from two tables depending on multiple where clauses

A query to get a list of all crimes with reason and the name of both the criminal and the police, that's what I am trying to achieve.
This is what I've tried:
SELECT crimes.user_police_id, crimes.user_criminal_id, crimes.reason, users.name
FROM `users`
INNER JOIN `crimes` on users.id = crimes.user_criminal_id
That returns only the name of the criminal...
Table structure
CREATE TABLE users (
id INT(11) AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(24)
);
INSERT INTO users (name) VALUES ("Divergent");
INSERT INTO users (name) VALUES ("SomeGuy");
CREATE TABLE crimes (
id INT(11) AUTO_INCREMENT PRIMARY KEY,
user_criminal_id INT(11),
user_police_id INT(11),
reason VARCHAR(256)
);
INSERT INTO crimes (user_criminal_id, user_police_id, reason) VALUES (2, 1, "Corruption");
This works, but it gets only the police name
SELECT crimes.user_police_id, crimes.user_criminal_id, crimes.reason, c.name, p.name
FROM crimes
JOIN users c on c.id = crimes.user_criminal_id
JOIN users p on p.id = crimes.user_police_id
Join a with users a second time to get the police name.
SELECT crimes.user_police_id, crimes.user_criminal_id, crimes.reason, u1.name AS criminal u2.name AS police
FROM `users` AS u1
INNER JOIN `crimes` on u1.id = crimes.user_criminal_id
INNER JOIN `users` AS u2 ON u2.id = crimes.user_police_id
You need to double join. I prefer to query crimes so you'll understand why :
SELECT crimes.user_police_id, crimes.user_criminal_id, crimes.reason, c.name, p.name
FROM crimes
JOIN users c on users.id = crimes.user_criminal_id
JOIN users p on users.id = crimes.user_police_id

Referencing Two Tables in SQL DB

I'm new to SQL, how could I answer the following question? Do I use join?
Thank you so much.
What are the names and home cities for people searched for the word "drain"?
.schema is below:
CREATE TABLE `users` (
`id` INTEGER,
`name` VARCHAR,
`email` VARCHAR,
`city` VARCHAR,
`state` VARCHAR,
`last_visit` DATE,
`page_views` INTEGER,
PRIMARY KEY (`id`)
);
CREATE TABLE `search_terms` (
`id` INTEGER,
`word` VARCHAR,
PRIMARY KEY (`id`)
);
select u.name, u.city
from users u
inner join search_terms st on st.id = u.id
where st.word = 'drain'
I hope this helps.
You do need to join search_terms table with users table. I assume the search_terms.id is referencing to users.id.
Linking them both, will give us the results of the search terms for each user.
from there you can add more filters (WHERE conditions) to have more specific results.
So, to get the names and the home cities for each user you need to select name and city from users table, then join search_terms table and link id columns to know what words that each user has used in the search.
The query should be something like this :
SELECT name, city
FROM users
LEFT JOIN search_terms ON search_terms.id = users.id
WHERE
search_terms.word LIKE 'drain';
You could use a JOIN or a sub-query. Here's the basic strategy:
Get the id values that match what you're looking for
Look up any other info for those id values
.
SELECT name, city
FROM users usr
INNER JOIN search_terms stm ON usr.id = stm.id
WHERE stm.word = 'drain';
or ...
SELECT name, city
FROM users
WHERE id IN (SELECT id FROM search_terms WHERE word = 'drain');

Is it possible to do a search query and specify which matches not to return, in the same query?

I have a search box that returns values from multiple tables using the LIKE operator. However I now want to make sure that any matches where x = y are not returned. Is this possible to do in the same query?
My attempt:
$search_sql = "SELECT * FROM User
INNER JOIN UserSkills ON User.UserId = UserSkills.UserId
INNER JOIN Skills ON UserSkills.SkillId = Skills.SkillId
INNER JOIN UserTasks on User.UserId = UserTasks.UserId
WHERE Description LIKE '%".$value."%'
OR FName LIKE '%".$value."%'
OR LName LIKE '%".$value."%'
OR JobRole LIKE '%".$value."%'
OR UserId NOT IN (
SELECT UserID from UserTasks WHERE TaskID = $Task)
GROUP BY Description" or die(mysqli_error($con));
$search_query = mysqli_query($con, $search_sql);
if(mysqli_num_rows($search_query) !=0) {
$search_rs = mysqli_fetch_assoc($search_query);
}
This attempt returns false (no results). There are results to be found.
This is the previous version that successfully returns results, before adding the new table (UserTasks), and NOT IN:
$search_sql = "SELECT * FROM User
INNER JOIN UserSkills ON User.UserId = UserSkills.UserId
INNER JOIN Skills ON UserSkills.SkillId = Skills.SkillId
WHERE Description LIKE '%".$value."%'
OR FName LIKE '%".$value."%'
OR LName LIKE '%".$value."%'
OR JobRole LIKE '%".$value."%'
GROUP BY Description" or die(mysqli_error($con));
If you want to exclude records where x = y, you would need to add something like AND x <> y (x does not equal y) to your WHERE. Keep in mind that you would need to parenthesize the WHERE components based on your intention when combining ANDs and ORs.
Without seeing the data in your tables I'm not sure why you currently are not getting results. My recommendation would be to start taking pieces out until you get the result you expect, and then adding them back testing your expectations until something doesn't match.
A few other notes:
The or die(..) here I don't think does anything, since the left side is simply string concatenation which I don't think could fail. You are not actually doing any MySQL related work when assigning to $search_sql.
I would recommend looking into parameterized queries rather than using string concatenation and variable interpolation to generate your query. It will be a bit more work initially, but tends to be easier to maintain and reason about and can easily be more secure.
I think INNER JOIN and JOIN have no functional difference, so I would pick one or the other and be consistent.
Using SELECT * when grouping by something has the potential to give unexpected/inconsistent results. Any column that is not in either the GROUP BY clause or contained in an aggregate will return the value on the first row of the group, but first is not guaranteed to be consistent (unless you ORDER). Other DBMS's (Oracle and SQL Server, at least, I think) actually forbid this behavior.
Update
SELECT Description
FROM User u
JOIN UserSkills us ON u.UserId = us.UserId
JOIN Skills s ON us.SkillId = s.SkillId
WHERE
(
Description LIKE '%".$value."%'
OR FName LIKE '%".$value."%'
OR LName LIKE '%".$value."%'
OR JobRole LIKE '%".$value."%'
)
AND UserId NOT IN (
SELECT UserID
FROM User u
JOIN UserTasks ut ON u.UserId = ut.UserId
WHERE TaskID = $Task
)
GROUP BY Description
Update with schema and data
CREATE TABLE users (
id INT NOT NULL,
first VARCHAR(16),
last VARCHAR(16),
PRIMARY KEY (id)
);
CREATE TABLE skills (
id INT NOT NULL,
description VARCHAR(64) NOT NULL,
job_role VARCHAR(64) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE user_skills (
user_id INT NOT NULL,
skill_id INT NOT NULL,
FOREIGN KEY user_skills_user (user_id) REFERENCES users (id),
FOREIGN KEY user_skills_skill (skill_id) REFERENCES skills (id)
);
CREATE TABLE user_tasks (
user_id INT NOT NULL,
task_id INT NOT NULL,
PRIMARY KEY (user_id,task_id),
FOREIGN KEY user_tasks_user (user_id) REFERENCES users (id)
);
INSERT INTO users VALUES (0,'FA','LA');
INSERT INTO users VALUES (1,'FB','LB');
INSERT INTO users VALUES (2,'FC','LC');
INSERT INTO skills VALUES (0,'Skill Description A','Job Role A');
INSERT INTO skills VALUES (1,'Skill Description B','Job Role B');
INSERT INTO skills VALUES (2,'Skill Description C','Job Role C');
INSERT INTO user_skills VALUES (0,0);
INSERT INTO user_skills VALUES (0,1);
INSERT INTO user_skills VALUES (0,2);
INSERT INTO user_skills VALUES (1,0);
INSERT INTO user_skills VALUES (1,1);
INSERT INTO user_skills VALUES (2,1);
INSERT INTO user_tasks VALUES (0,1);
INSERT INTO user_tasks VALUES (1,2);
INSERT INTO user_tasks VALUES (2,1);
SELECT *
FROM users u
JOIN user_skills us ON u.id = us.user_id
JOIN skills s ON us.skill_id = s.id
WHERE
(
s.description LIKE ''
OR u.first LIKE '%F%'
OR u.last LIKE ''
OR s.job_role LIKE ''
)
AND u.id NOT IN (
SELECT user_id
FROM user_tasks
WHERE task_id = 2
)
;
This should return 4 records, 3 for FA LA by 3 skills, and one for FC LC by 1 skill. FB LB is not returned because it is assigned task 2. Changing to task_id = 1 should return 2 records, FB LB by 2 skills.

mysql find recent user comments (part 2)

Cont. on mysql find recent user comments
Again, 2 tables:
create table user(
userID int auto_increment,
userName varchar(10),
userCreatedDate timestamp,
primary key(userID)
);
create table comment(
commentID int auto-increment,
userID int,
comment varchar(100),
primary key(commentID),
foreign key(userID) references user(userID)
);
This time the userCreateDate and commentID value is different from part 1.
And I want to find recent comment from the database.
My output like the following:
Here is the query that I tried:
select u.userID, max(c.commentID) as commentID, c.comment, u.userCreatedDate
from comment c
left join user u on c.userID = u.userID
group by u.userID
order by u.userCreateDate desc
However, I can't get my output.
Can someone help me?
You can do this as below and is called group wise max/min http://dev.mysql.com/doc/refman/5.7/en/example-maximum-column-group-row.html
select u.*,c.comment from user u
join comment c on c.userID = u.userID
join (
select max(commentID) as commentID,userID
from comment group by userID
)c1 on c1.commentID = c.commentID
try this query :-
select u.userID,commentID, c.comment, u.userCreatedDate
from comment c
left join user u on c.userID = u.userID
WHERE commentID=(SELECT MAX(c2.commentID)
FROM comment c2
WHERE c.userID = c2.userID)