Returning duplicate results SQL? - php

I have the following query:
SELECT DISTINCT
SQL_CALC_FOUND_ROWS
unr.RequestID,
unr.UnRead,
unr.FilterID,
r.GroupID,
r.Year,
rv.Bounty
FROM (users_notify_requests as unr, requests_votes as rv)
JOIN requests AS r ON r.ID = unr.RequestID
WHERE unr.UserID = 1 ORDER BY unr.RequestID DESC LIMIT 50
This should return only 2 rows, as there are only two requests where unr.UserID = 1, however it returns 10. 5 versions of the first, and 5 versions of the second, completely identical (respectively). Any idea as to why this might be happening?
EDIT: MySQL version 5.5.29, as requested.
EDIT 2: The print_r() dump: http://pastebin.com/BXujnEpx. The result has incorrect bounty for the given IDs, so something is pretty off with the query.

You're not joining on rv with any criteria, so every row in that table will be returned: It could be that this is the cause of your extra rows.
You probably need a line such as the following (guessing at column names as I don't have your schema):
INNER JOIN request_votes rv ON rv.requestId = r.id

you are joining two tables based on cartesian product or cross join .
FROM (users_notify_requests as unr, requests_votes as rv)
And then you are using a inner join.
JOIN requests AS r ON r.ID = unr.RequestID
To solve the problem you should use inner join in both cases.
Cartesian join is when you join every row of one table to every row of another table. if 1st table contain x rows and y rows in 2nd one the result set will be x*y rows.
SELECT DISTINCT
SQL_CALC_FOUND_ROWS
unr.RequestID,
unr.UnRead,
unr.FilterID,
r.GroupID,
r.Year,
rv.Bounty
FROM users_notify_requests as unr
JOIN requests_votes as rv ON rv.RequestID = unr.RequestID
JOIN requests AS r ON r.ID = unr.RequestID
WHERE unr.UserID = 1 ORDER BY unr.RequestID DESC LIMIT 50

Related

JOIN shows data twice in mysql?

SELECT *
FROM `all_salary_slip_details` `assd`
INNER JOIN `employee_master` `em` ON `assd`.`EMPLOYEE_ID` = `em`.`EMPLOYEE_ID`
INNER JOIN `employee_details` `ed` ON `assd`.`EMPLOYEE_ID` = `ed`.`EMPLOYEE_ID`
INNER JOIN `organizations` `o` ON `o`.`ORG_ID` = `assd`.`ORG_ID`
INNER JOIN `months` `mo` ON `mo`.`id` = `assd`.`PAY_MONTH`
This is my query to fetch data from db. I have only 4 rows but in my view I get 48 rows. I dont understand why this happens.
You need to use a GROUP BY clause on a column, then your query will only return a single row for each unique entry in that column.
In your case I'm guessing it would make sense to use the employee name.
What result does this query output?
SELECT assd.PAY_YEAR, assd.PDF, em.EMPLOYEE_NAME, o.ORG_NAME, mo.month_name
FROM all_salary_slip_details assd
INNER JOIN employee_master em
ON assd.EMPLOYEE_ID = em.EMPLOYEE_ID
INNER JOIN organizations o
ON o.ORG_ID = assd.ORG_ID
INNER JOIN months mo
ON mo.id = assd.PAY_MONTH
GROUP BY em.EMPLOYEE_NAME
You will get 1 row returned in your query for each record in a joined table that matches the join condition.
So, if you have multiple records in any one of the employee_master, employee_details, organisations or months tables that match those in all_salary_slip_details you will get additional rows returned. Look at the rows returned, you will see differences in each and where they differ will tell you where the join condition for the table isn't specific enough to return a single row.

Selecting the first lowest from a result set

I have this SQL statement:
SELECT A.service_code_id, A.job_line_id, B.job_code, B.description, C.type_text, D.job_no, E.status, C.priority
FROM job_line_services as A
INNER JOIN job_codes as B
ON A.service_code = B.job_code
INNER JOIN job_type as C
ON B.service_type_id = C.unique_id
INNER JOIN job_lines as D
ON A.job_line_id = D.unique_id
INNER JOIN jobs_in_list as E
ON D.job_no = E.job_no
WHERE E.status = "In Progress" AND D.job_no = 17301
ORDER BY C.priority
This works fine and it returns to following results:
This is great, however I would like to only select the first result, ordered by lowest priority. In the image it would be the first row.
I have tried using GROUP BY and ORDER BY, Selecting DISTINCT job_no, Using the MIN() function on different tables, but I always end up with last row in those results, that one with the priority of 4.
How can I get just the first result in this result set?

Grouping and chunking by value in MySQL

I have a simple raw SQL query which is returning data like this:
SELECT
a.id, count(ah.id) as count
FROM
`table_name_one` as a
JOIN
`table_name_two` as ah
ON
a.id=ah.answer_id
WHERE
a.user_id = 7178
AND a.created_at BETWEEN '2017-05-01' AND '2017-05-28'
GROUP BY a.id
I want to group this result by two groups.
Group One will contain data when revisionCount == 1 and Group Two will contain rest of the data. I can do this by using a loop but if I can do this by SQL query it will be good for me.
Can anybody help me out from here?
If you want to construct a list of the two sets of values, you can do so in one query:
SELECT GROUP_CONCAT(CASE WHEN cnt = 1 THEN id END) as id_1s,
GROUP_CONCAT(CASE WHEN cnt > 1 THEN id END) as id_2plus
FROM (SELECT a.id, count(ah.id) as cnt
FROM `table_name_one` a JOIN
`table_name_two` as ah
ON a.id = ah.answer_id
WHERE a.user_id = 7178 AND
a.created_at BETWEEN '2017-05-01' AND '2017-05-28'
GROUP BY a.id
) a;
This puts the values into comma-delimited lists. MySQL by default limits the length to 1,024 characters. If the lists are longer, then you can run the query twice to generate each . . . or just order by cnt and check the value when you are reading the return values.
You can use having clause to achieve this. I would recommend you to read online for more info on having.
Below query will give you all records with count exactly as 1:
SELECT
a.id, count(ah.id) as count
FROM
`table_name_one` as a
JOIN
`table_name_two` as ah
ON
a.id=ah.answer_id
WHERE
a.user_id = 7178
AND a.created_at BETWEEN '2017-05-01' AND '2017-05-28'
GROUP BY a.id
HAVING count(ah.id) = 1
You can tweak the having condition in the above query to get the second set.
I assume you need 2 result sets as arrays in php, for which you need to write 2 queries separated by semicolon.
Query 1 where revision count =1;
Query 2 where revision count >1;
Hope this helps

Faster search query for IN statement with SELECT in MySQL

I'm currently doing some query for my app and I need to get the nearest store on my current position and to do this first I need to get all the item that has the same name then get it's information and trim down that query. Now I used IN statement for this but since the items being searched are also based on a list I need to make use of another select for this here is my code so far:
select *
from product p,
store s,
branches b
where 1 = 1
and b.idproduct = p.idproduct
and p.store = s.idstore
and common_name IN(SELECT p.common_name
FROM shopping_list_content s, product p
WHERE 1 =1
AND s.iditem = p.idproduct
AND s.idlist =$listid)
Now it works as I wanted it to be but I wanted it to do the query faster than this. For now it takes more than 3 seconds for this query to run faster than this. much better if it is less than a second. Any other option I can use for this?
MySQL has difficulty optimising subqueries, when you write something like:
SELECT *
FROM T
WHERE T.ID (SELECT ID FROM T2);
It is sometimes rewritten as
SELECT *
FROM T
WHERE EXISTS
( SELECT 1
FROM T2
WHERE T.ID = T2.ID
);
The subquery is then executed once per row in T, whereas if you write:
SELECT T.*
FROM T
INNER JOIN
( SELECT DISTINCT ID
FROM T2
) T2
ON T2.ID = T.ID;
Your result set will be the same, but MySQL will first fill an in memory table with the results of the subquery and hash it on T2.ID, it then just needs to lookup against this hash table for each row in T.
Which behaviour you want really depends on how much data you are expecting from each table/subquery. If you have 1 million rows in T2, and 10 in T then there is no point in filling a temporary table with 1 million rows, only to subsequently only use it 10 times, whereas if you have a large number of rows in T and only a small amount in T2 the additional cost of materialising the subquery will be beneficial in the long run.
Another thing to point out (which has no impact on performance), the JOIN syntax you are using is the ANSI 89 syntax and was replaced by ANSI 92 explicit JOIN syntax over 20 years ago. Although directed at SQL Server, I think this article summarises the reasons to switch to the newer join syntax very well. Making your final query:
SELECT *
FROM product p,
INNER JOIN store s
ON p.store = s.idstore
INNER JOIN branches b
ON b.idproduct = p.idproduct
INNER JOIN
( SELECT DISTINCT p.common_name
FROM shopping_list_content s
INNER JOIN product p
ON s.iditem = p.idproduct
WHERE s.idlist =$listid
) s
ON s.common_name = p.common_name;
N.B. Most of the above does not apply if you are using MySQL 5.6.5 or later. In this version they introduced more Subquery Optimization that solved a lot of the above issues
This is your query fixed up to use proper join syntax:
select *
from product p join
store s
on p.store = s.idstore join
branches b
on b.idproduct = p.idproduct
where p.common_name IN (SELECT p.common_name
FROM shopping_list_content slc join
product p
ON slc.iditem = p.idproduct AND
slc.idlist = $listid
);
Assuming that the same common_name does not appear on multiple products and that shopping_list_content has no duplicate rows, you can replace this with a simple join:
select *
from product p join
store s
on p.store = s.idstore join
branches b
on b.idproduct = p.idproduct join
shopping_list_content slc
on slc.iditem = p.idproduct and
slc.idlist = $listid;
However, those assumptions may not be true. In that case, changing the subquery to use exists may help performance:
select *
from product p join
store s
on p.store = s.idstore join
branches b
on b.idproduct = p.idproduct
where exists (SELECT 1
FROM shopping_list_content slc join
product p2
on slc.iditem = p2.idproduct AND
slc.idlist = $listid
WHERE p.common_name = p2.common_name
);
For this latter query, an index on product(common_name, idproduct) along with shopping_list_content(iditem, idlist) should help.

get count of posts based on count(*)

i am trying to get number of posts that i have
Here is my query
$Query="
SELECT t.*,u.*,c.*
FROM posts as t
LEFT JOIN relations as r on r.post_id = t.post_id
LEFT JOIN users as u on t.auther_id = u.auther_id
LEFT JOIN categories as c on c.cate_id = r.cate_id
GROUP BY t.post_id
";
$Query=mysql_query($Query);
$numberOfPosts=mysql_num_rows($Query);
This query is works very well
but i am trying to convert it, i want make it faster
i want use count(*) instead of t.*
because when i use t.*, it gets the full data of posts and categories
but i want to get count only, So i decided to use count(*) but i don't know how to use it with query like this
Edit
i've replaced SELECT t.*,u.*,c.* with SELECT count(t.*)
But i got mysql Error Warning: mysql_fetch_assoc(): supplied argument
Edit 2:
i am trying SELECT count(t.post_title)
I Got this results
Array ( [count(t.post_id)] => 10 )
But i have only 2 posts!
$Query="
SELECT t.*,u.*,c.*
FROM posts as t
LEFT JOIN relations as r on r.post_id = t.post_id
LEFT JOIN users as u on t.auther_id = u.auther_id
LEFT JOIN categories as c on c.cate_id = r.cate_id
GROUP BY t.post_id
";
$Query=mysql_query($Query);
$numberOfPosts=mysql_num_rows($Query);
Let's take a step back and analyze this query for a moment.
You're selecting everything from three out of four tables used in the query. The joins create some logic to limit what you select to the proper categories, authors, etc. At the end of the day you are getting a lot of data from the database, then in PHP simply asking it how many rows were returned (mysql_num_rows). Instead, what #Dagon is trying to suggest in comments, is that you have MySQL simply count the results, and return that.
Let's refactor your query:
$query = "
SELECT COUNT(t.post_id) AS qty
FROM posts as t
LEFT JOIN relations AS r ON r.post_id = t.post_id
LEFT JOIN users AS u ON t.auther_id = u.auther_id
LEFT JOIN categories AS c ON c.cate_id = r.cate_id
GROUP BY t.post_id
";
$result = mysql_query($query);
$result_row = mysql_fetch_assoc($result);
$numberOfPosts = $result_row['qty'];
(You could also use Barattlo's custom execute_scalar function to make it more readable.)
I would need to see your table structures to be of more help on how to optimize the query and get the desired results.
try doing this:
$Query="
SELECT count(t.*) as count_all
FROM posts as t
LEFT JOIN relations as r on r.post_id = t.post_id
LEFT JOIN users as u on t.auther_id = u.auther_id
LEFT JOIN categories as c on c.cate_id = r.cate_id
GROUP BY t.post_id
";
$Query=mysql_query($Query);
$numberOfPosts=mysql_num_rows($Query);
You want to do
SELECT count(t.id) AS count FROM ....
//do query with PDO or whatever you are using
$rows = mysql_fetch_assoc();
$num_rows = $rows['count'];
You should probably simply use
SELECT count(*) as postingcount FROM posts
Why?
Because you do not have a WHERE clause, so there are no restrictions. Your JOINS do not ADD more rows to the resultset, and in the end your GROUP BY merges every duplicate occurance of a post_id that might have occurred because of joining back into one row. The result should only be counted, so assuming that the real number you want to know is the number of data sets inside the table posts, you do not need any join, and doing count(*) really is a very fast operation on tables in MySQL.
Remember to check if mysql_query returns false, because then you have to check mysql_error() and see why your query has an error.

Categories