MySQL showing duplicate rows - mysql

I have two tables, people and comment.
Table: people
+-------------------+----------------+-------------+------+
| id | cn | en | dob | role |
+-------------------+----------------+-------------+------+
| 1 | ChineseName | EnglishName | 1989-03-02 | 0 |
+-------------------+----------------+-------------+------+
| 2 | ChineseName2 | EnglishName2 | 1923-06-12 | 1 |
+-------------------+----------------+-------------+------+
Table: comment
+----+--------+----------+-------------------+---------------------+
| id | owner | owner_id | creator_person_id | comment |
+----+--------+----------+-------------------+---------------------+
| 1 | PERSON | 2 | 1 | Some comments here |
+----+--------+----------+-------------------+---------------------+
| 2 | TRANSAC| 1 | 1 | Comments |
+----+--------+----------+-------------------+---------------------+
| 3 | PERSON | 1 | 1 | Comments here |
+----+--------+----------+-------------------+---------------------+
When I execute the query:
SELECT comments.comment,
creator_person.id AS creator_id,
creator_person.cn AS creator_cn,
creator_person.en AS creator_en,
creator_person.dob AS creator_dob,
creator_person.role AS creator_role
FROM people, comments
JOIN people AS creator_person
ON comments.creator_person_id = creator_person.id AND comments.owner = 'PERSON' AND comments.owner_id = 1
ORDER BY people.id
I suppose it will return me only 1 row, however I'm getting a duplicate of that row:
+------------------+------------+-------------+-------------+-------------+--------------+
| comment | creator_id | creator_cn | creator_en | creator_dob | creator_role |
+------------------+------------+-------------+-------------+-------------+--------------+
| Comments here | 1 | ChineseName | EnglishName | 1989-03-02 | 0 |
+------------------+------------+-------------+-------------+-------------+--------------+
| Comments here | 1 | ChineseName | EnglishName | 1989-03-02 | 0 |
+------------------+------------+-------------+-------------+-------------+--------------+

you joining people table twice. So you can modify your query to become:
SELECT comments.comment,
creator_person.id AS creator_id,
creator_person.cn AS creator_cn,
creator_person.en AS creator_en,
creator_person.dob AS creator_dob,
creator_person.role AS creator_role
FROM comments
INNER JOIN people AS creator_person
ON comments.creator_person_id = creator_person.id
AND comments.owner = 'PERSON' AND comments.owner_id = 1
ORDER BY creator_person.id
also add distinct keyword if same problem occur like:
SELECT distinct comments.comment, ...
...
...

You should change:
FROM people, comments
to:
FROM comments
Since you are joining on the people table you do not need to include it in the FROM clause.
You will also need to update your order clause to reflect the alias name you gave the people table:
ORDER BY creator_person.id

You have an extra join in there (an implicit one in people,comments since you are after doing an inner join with people you should remove the first).
select comments.comment,
creator_person.id as creator_id,
creator_person.cn as creator_cn,
creator_person.en as creator_en,
creator_person.dob as creator_dob,
creator_person.role as creator_role
from comments
inner join people as creator_person
on comments.creator_person_id = creator_person.id
where comments.owner = 'PERSON' and comments.owner_id = 1
order by creator_person.id;
sqlfiddle demo
I also moved the other validations from the on clause to the where clause. In this case, you are doing a inner join, it wouldn't make a difference, but this is a bad habit and could make you do weird things when you use left/right joins in the future

Related

How can I merge rows from two joined tables?

There are two tables, which can be joined, and the relationship is 1 to many. I wish the rows of results to be merged.
For example:
Table 1: contacts
.------------.----------.
| contact_id | username |
:------------+----------:
| 1 | user1 |
:------------+----------:
| 2 | user2 |
:------------+----------:
| 3 | user3 |
'------------'----------'
Table 2: documents
.-------------.------------.----------.
| document_id | contact_id | filename |
:-------------+------------+----------:
| 1 | 1 | abc.txt |
:-------------+------------+----------:
| 2 | 1 | bcd.txt |
:-------------+------------+----------:
| 3 | 1 | cde.txt |
:-------------+------------+----------:
| 4 | 2 | 123,txt |
:-------------+------------+----------:
| 5 | 2 | 234.txt |
:-------------+------------+----------:
| 6 | 3 | xyz.txt |
'-------------'------------'----------'
The result I wish I can get:
.------------.----------.---------------------------.
| contact_id | username | filenames |
:------------+----------+---------------------------:
| 1 | user1 | abc.txt, bcd.txt, cde.txt |
:------------+----------+---------------------------:
| 2 | user2 | 123.txt, 234.txt |
:------------+----------+---------------------------:
| 3 | user3 | xyz.txt |
'------------'----------'---------------------------'
Updated:
SELECT c.contact_id, c.username, GROUP_CONCAT(d.filename) as filenames
FROM contacts c
LEFT JOIN documents d
ON c.contact_id = d.contact_id
GROUP BY c.contact_id
You should really post your attempts with your question, so that we can see what you have tried. In that way, it will be easy to push you in the right direction, as well as give the rest of us the impression that you have put some effort into the matter before asking the question. Stackoverflow is not a coding service.
To answer your question,
What you would like to do in this case, is to perform an INNER JOIN on your two tables, and have the MYSQL function, GROUP_CONCAT();, in your SELECT statement.
When you look at your two tables, you have a coherent id (contact_id) that you should use in your INNER JOIN to link your two tables together.
You then, at the end, need to perform a GROUP BY to group your results accordingly, i.e. to group the results by contact_id.
Your SQL would look something like this:
SELECT
tbl_contacts.contact_id,
tbl_contacts.username,
GROUP_CONCAT(tbl_documents.filename) as file_name
FROM
tbl_contacts
INNER JOIN
tbl_documents ON tbl_contacts.contact_id = tbl_documents.contact_id
GROUP BY
tbl_contacts.contact_id
Working SQL fiddle

How to get all rows from a database where foreign key is equal to the result of a query?

So I have a table of "Articles" that has a foreign key of "category_id".
Articles
| id | title | pub_date | category_id |
-----------------------------------
| 0 | abc | 23423443 | 1 |
| 1 | def | 23423423 | 2 |
| 2 | ghi | 24234234 | 1 |
| 3 | jkl | 23423424 | 3 |
| 4 | mop | 23432435 | 2 |
Categories
| id | title |
----------------
| 1 | News |
| 2 | Feature |
| 3 | Review |
I have the title of a category.
I would like to, in one query, ascertain the id of said category and use it to return articles where the category_id = id and publish date is less than the current date time.
Is this possible?
I am using Postgres but I am looking at this from an academic standpoint so answers in any SQL dialect would be fine as I am happy to do the translation myself for the education.
select *
from Articles
where pub_date <= now() and
category_id = (select id from Categories where title="TITLE")
Is this the kind of thing you're after? I don't know if this is PostgresSQL or not.
select A.id, A.title, A.pub_date, C.id, C.title
from Articles A join Categories C on A.category_id=C.id
where C.title = MY_CATEGORY_TITLE and
a.pub_date < CURRENT_DATE_TIME
I'm ignoring the need for indexes, orders etc.
it's just a simple join
try
SELECT A.id,A.title,A.pub_date,A.category_id,
C.title
FROM Articles A
INNER JOIN Categories C
ON A.category_id = C.id
WHERE pub_date < NOW()
AND C.title = "your title"

select rows where related record doesn't exist

I need to retrieve rows from a mysql database as follows: I have a contract table, a contract line item table, and another table called udac. I need all contracts which DO NOT have a line item record with criteria based on a relationship between contract line item and udac. If there is a better way to state this question, let me know.
Table Structures
----contract--------------------- ---contractlineitem-----------
| id | customer_id | entry_date | | id | contract_id | udac_id |
--------------------------------- ------------------------------
| 1 | 1234 | 2010-01-01 | | 1 | 1 | 5 |
| 2 | 2345 | 2016-01-31 | | 2 | 1 | 2 |
--------------------------------- | 3 | 1 | 1 |
| 4 | 2 | 4 |
| 5 | 2 | 2 |
------------------------------
---udac----------
| id | udaccode |
-----------------
| 1 | SWBL/R |
| 2 | SWBL |
| 3 | ABL/R |
| 4 | ABL |
| 5 | XRS/F |
-----------------
Given the above data, contract 2 would show up but contract 1 would not, because it has contractlineitems that point to udacs that end in /F or /R.
Here's what i have so far, but it's not correct.
SELECT c.*
FROM contract c
JOIN contractlineitem cli
ON c.id = cli.contract_id
WHERE c.entry_timestamp > '2016-01-01 00:00:00'
AND NOT EXISTS (
SELECT cli.id
FROM contractlineitem cli_i
JOIN udac u
ON cli_i.udac_id = u.id
WHERE u.udaccode LIKE '%/F' OR u.udaccode LIKE '%/R'
AND cli_i.contract_id = cli.contract_id);
Tom's comment that your WHERE clause is wrong may be the problem you are chasing. Plus, using a correlated subquery may be problematic for performance if the optimizer can't figure out a better way to do it.
Here is the better way to do it using an OUTER JOIN:
SELECT c.*
FROM contract c
JOIN contractlineitem cli
ON c.id = cli.contract_id
LEFT OUTER JOIN udac u
ON ( u.id = cli.udac_id
AND ( u.udaccode LIKE '%/F' OR u.udaccode LIKE '%/R' ) )
WHERE c.entry_timestamp > '2016-01-01 00:00:00'
AND u.id IS NULL
Try that out and see if it does what you want. The query essentially does what you stated: It tries to join to udac where the code ends in '/F' or '/R', but then it only accepts the ones where it can't find a match (u.id IS NULL).
If the same row is returned multiple times incorrectly, throw a distinct on the front.

Getting "subquery returns more than one row" error whenever trying to join a column from another table as alias?

I am stuck with this problem for a whole 2 days. I have a users table and it contains:
+--------+--------+----------+--------------------------------------------+
| userId | name | username | profile_pic |
+--------+--------+----------+--------------------------------------------+
| 1 | john | john123 | http://localhost/profile_pic/user1pic.jpg |
| 2 | andrew | andrew | http://localhost/profile_pi/user2pic.jpg |
| 3 | doe | doe | http://localhost/profile_pic/user3pic.jpg |
+--------+--------+----------+--------------------------------------------+
I have another table called userpost which contains:
+--------+--------+-------------+----------------------------+
| postId | userId | postMessage | postImage |
+--------+--------+-------------+----------------------------+
| 1 | 1 | "Hey" | http://localhost/post1.jpg |
| 2 | 3 | "Add me" | http://localhost/post2.jpg |
| 3 | 2 | "boring" | http://localhost/post3.jpg |
+--------+--------+-------------+----------------------------+
userId is refrenced to users.userId. I am trying to join profile_pic to userpost but mysql is returning error. Here is what I am doing:
SELECT *, (SELECT profile_pic FROM users
INNER JOIN userpost on users.userId = userpost.userId) as profile_pic FROM userpost
But getting Subquery returns more than 1 row error
I know I am doing something stupid with the query. I just want something like this:
+--------+--------+-------------+----------------------------+--------------------------------------------+
| postId | userId | postMessage | postImage | profile_pic |
+--------+--------+-------------+----------------------------+--------------------------------------------+
| 1 | 1 | "Hey" | http://localhost/post1.jpg | http://localhost/profile_pic/user1pic.jpg |
| 2 | 3 | "Add me" | http://localhost/post2.jpg | http://localhost/profile_pic/user3pic.jpg |
| 3 | 2 | "boring" | http://localhost/post3.jpg | http://localhost/profile_pi/user2pic.jpg |
+--------+--------+-------------+----------------------------+--------------------------------------------+
I am having a meeting tomorrow to showcase my prototype app. Help will be appreciated.
You are using a sub query not a join. When using subquery in the select, you have to make sure it returns exacly one row like
SELECT COL1,COL2,(SELECT 1) from YourTable
Or by using a correlated query, which I assume was your purpose but is not required since its from the same table as you select, so just use a simple join:
SELECT s.*, t.profile_pic
FROM users t
INNER JOIN userpost s on t.userId = s.userId
Try this...
SELECT *,
(SELECT top 1 profile_pic FROM users a
where a.userId = b.userId order by b.postId desc) as profile_pic
FROM userpost b
But i am not sure, the above query returns the desired profile picture.
You are not using the sub-query correctly.you should make sure that sub-query will return only one row.
Below is the Query that you require.
Select up.postId,u.userId,up.postMessage,up.postImage,u.profile_pic from user u
inner join userpost up on u.userId=up.userId

Select a MYSQL result where tables one and two match and two and three do not match

I have a two tables with users in an old format and a new format. I want to match the users with the old format to a separate table, then exclude all users who also show up in the new user format table. My data is like this:
Table newUsers:
+----+-------+-------+----------+
| id | oldid | first | last |
+----+-------+-------+----------+
| 1 | 10 | John | Kennedy |
| 2 | 66 | Mitch | Kupchak |
+----+-------+-------+----------+
Table posts:
+----+---------+
| id | user_id |
+----+---------+
| 1 | 10 |
| 1 | 66 |
| 1 | 88 |
| 2 | 88 |
| 2 | 28 |
| 3 | 10 |
+----+---------+
Table oldUsers:
+----+----------+-------+----------+
| id | username | first | last |
+----+----------+-------+----------+
| 10 | A | John | Kennedy |
| 66 | B | Mitch | Kupchak |
| 88 | C | Dale | Earnhardt|
+----+----------+-------+----------+
Result wantend:
+----+----------+-------+----------+
| id | username | first | last |
+----+----------+-------+----------+
| 88 | C | Dale | Earnhardt|
+----+----------+-------+----------+
I want to select my result by specifying: posts.id = 1 and posts.user_id = oldUsers.id and newUsers.oldid != oldUsers.id so that I only receive oldUser.id equaling 88 because he wasn't in the newUsers list.
I have tried all kinds of JOINS and SUBQUERIES. I keep getting all of the results and not the results minus corresponding entries in the newUsers table.
select * from oldusers where id in
select * from
(select id from oldusers where id in
select distinct userid from posts where id=1)
where id not in (select oldid from newusers);
Here is a way to do it
select
o.* from oldUsers o
left join newUsers n on o.id = n.oldid
left join posts p on n.oldid = p.user_id or o.id = p.user_id
where n.id is null and p.id= 1;
For better performance add the following indexes
alter table newUsers add index oldid_idx(oldid);
alter table posts add index user_post_idx (id,user_id);
I ended up finding my answer on my own and then came here to find others tried. Abhik's code did work, but was too inefficient to use. I ended up playing with my own code and IS NULL until I found something that was much more efficient.
select o.* from posts p, oldUsers o
LEFT JOIN newUsers n ON o.id = n.oldid
WHERE p.user_id = o.id AND p.id = 1 AND n.id IS NULL
Executes in .0044 seconds. Something I can use on a production site.
With indexes added from previous answer it now executes in .001x seconds so definately going with my own code.