Where condition not working with IN Clause - php

I have a query. In this query I am using a sub query to get the data from the same table with different condition and in the main query I am mentioning the ids that are used to get data in the sub query and putting a condition that the values that are being considered in the main query with the ids should not be empty. If I use a single ID with IN clause in the main query, my query works fine but if I use multiple ids in the main query and add the clause that the values should not be empty, the query does not give me the desired result.Here is my query
SELECT e.id AS `Personal Number`,
e.date AS `Date`,
CONCAT(ep.firstname,' ',ep.lastname) AS `Employee Name`,
IF(ep.sex='M','Male','Female') AS sex,
DATE_FORMAT(p.birthdate,'%m/%d/%Y')AS birthdate,
(SELECT `value` FROM employee_data WHERE history=87 AND DATE=e.date) AS `A`,
(SELECT `value` FROM employee_data WHERE history=603 AND DATE=e.date) AS `B`,
(SELECT `value` FROM employee_data WHERE history=82 AND DATE=e.date) AS `C`,
(SELECT `value` FROM employee_data WHERE history=86 AND DATE=e.date) AS `D`
FROM tbl e
INNER JOIN employee ep ON e.id = ep.id
INNER JOIN tbl2 ap ON ap.date=e.date
INNER JOIN employee_data AS phd ON e.date = phd.date
WHERE (phd.history IN(82,87,603,86) AND phd.value!='') AND ap.date BETWEEN '2013-01-01' AND '2013-09-01'AND e.status!='cancelled'.
I am out of ideas that what to do with this query. Can anyone help. Thanks in advance

Since you fetch values with filter in select clause, do a group by and get max() value in select clause.
max (SELECT `value` FROM employee_data WHERE history=87 AND DATE=e.date) AS `A`,
max (SELECT `value` FROM employee_data WHERE history=603 AND encounter_nr=e.encounter_nr) AS `B`,
max (SELECT `value` FROM employee_data WHERE history=82 AND encounter_nr=e.encounter_nr) AS `C`,
max (SELECT `value` FROM employee_data WHERE history=86 AND encounter_nr=e.encounter_nr) AS `D`
FROM

Here is your problem. Your query is producing four rows when you have the where clause. But, I suspect that you are just expecting one row, or at least, just one row per id.
I think this is the query that you want:
SELECT e.id AS `Personal Number`, e.date AS `Date`,
CONCAT(ep.firstname,' ',ep.lastname) AS `Employee Name`,
IF(ep.sex='M','Male','Female') AS sex,
DATE_FORMAT(p.birthdate,'%m/%d/%Y') AS birthdate,
max(case when history = 87 then value end) as A,
max(case when history = 603 then value end) as B,
max(case when history = 82 then value end) as C,
max(case when history = 86 then value end) as D
FROM tbl e INNER JOIN
employee ep
ON e.id = ep.id INNER JOIN
tbl2 ap ON ap.date = e.date INNER JOIN
employee_data phd
ON e.date = phd.date
WHERE phd.history IN (82, 87, 603, 86) AND phd.value <> '' AND
ap.date BETWEEN '2013-01-01' AND '2013-09-01' AND
e.status <> 'cancelled'
group by e.id;
This should return one row for each employee.
EDIT:
It occurs to me that you might not want an aggregation. You can follow your original approach by removing the join to employee_data in the outer query:
SELECT e.id AS `Personal Number`, e.date AS `Date`,
CONCAT(ep.firstname,' ',ep.lastname) AS `Employee Name`,
IF(ep.sex='M','Male','Female') AS sex,
DATE_FORMAT(p.birthdate,'%m/%d/%Y') AS birthdate,
(SELECT `value` FROM employee_data WHERE history=87 AND DATE=e.date) AS `A`,
(SELECT `value` FROM employee_data WHERE history=603 AND DATE=e.date) AS `B`,
(SELECT `value` FROM employee_data WHERE history=82 AND DATE=e.date) AS `C`,
(SELECT `value` FROM employee_data WHERE history=86 AND DATE=e.date) AS `D`
FROM tbl e INNER JOIN
employee ep
ON e.id = ep.id INNER JOIN
tbl2 ap ON ap.date = e.date
WHERE ap.date BETWEEN '2013-01-01' AND '2013-09-01'AND e.status <> 'cancelled'
HAVING A <> '' and B <> '' and C <> '' and D <> '';
The having clause is a MySQL trick that lets you refer to column aliases in the from clause. It does not imply aggregation in this case.
You would do this if you had an index employee_data(history, date).

Related

Mysql row number with order by very slow

I have this query
select courses.id, y.qs_world, courses.name_en as name,
courses.description_en as description,
source_link, courses.slug, fee, duration, courses.university_id, college_id,
study_level_id, application_fee, courses.currency_id
from courses
left join university_ranks as y on courses.university_id = y.university_id
and y.year = '2021'
left join universities on courses.university_id = universities.id
left join countries on countries.id = universities.country_id where courses.status = 1
order by ROW_NUMBER() OVER (PARTITION BY countries.id ORDER BY courses.id)
This query is taking too long to execute, but it is working well if I remove the last row.
I used indexing but nothing different.
The EXPLAIN notes are to Using temporary,Using filesort but I want to improve the query without using temporary or filesort
How can I achieve this?
UPDATE:
I tried this query but same speed
SELECT * FROM (
SELECT
`courses`.`id`,`courses`.`status`, `y`.`qs_world`, `courses`.`name_en` as `name`, `courses`.`description_en` as `description`,
`source_link`, `courses`.`slug`, `fee`, `duration`, `courses`.`university_id`, `college_id`,
`study_level_id`, `application_fee`, `courses`.`currency_id`, `countries`.`id` as country_id
FROM
courses
left join `university_ranks` as `y` on `courses`.`university_id` = `y`.`university_id`
and `y`.`year` = '2021'
left join `universities` on `courses`.`university_id` = `universities`.`id`
left join `countries` on `countries`.`id` = `universities`.`country_id`
) UserCourse where status = 1
order by ROW_NUMBER() OVER (PARTITION BY country_id ORDER BY id)
countries.id as country_id --> universities.country_id
then remove
left join `countries` ON `countries`.`id` = `universities`.`country_id`
Move where status = 1 into the inner query.
It seems like
order by ROW_NUMBER() OVER (PARTITION BY country_id ORDER BY id)
could be replaced by
ORDER BY country_id, id
Get rid of the outer query
Don't say LEFT unless the 'right' table might have a missing row. (It confuses the reader as to your intent.)

Specify columns in INNER JOINed table

comment table and post table both has column named user_id
I cannot specify both table's user_id
for using some if else condition later I need both the user_id as a different name (I'm trying to use AS).
I tried different way but query not working:
$sql="SELECT `post_id`, `comment_id`, `comment`, `user_id`, `username`,
`is_marked` `post`.`user_id` AS `p_uid` FROM `comment` INNER JOIN `user` ON
`comment`.`user_id` = `user`.`id` INNER JOIN `post` ON
`user`.`id`=`post`.`user_id` ORDER BY `comment_id` DESC";
$result = mysqli_query($con, $sql);
if ($result) {
while ($row=mysqli_fetch_assoc($result)) {
$post_user_id = $row['p_uid'];
You could alias table name with other name and get column. View example:
SELECT C.comment_id, U.user_id
FROM comment C INNER JOIN user u ON C.user_id = U.id
I would do:
SELECT c.post_id,
c.comment_id,
c.comment,
c.user_id AS c_uid,
c.username,
c.is_marked,
p.user_id AS p_uid
FROM comment c
INNER JOIN user u ON c.user_id = u.id
INNER JOIN post p ON c.user_id = p.id
ORDER BY c.comment_id DESC
Define aliases for the tables, and the selected fields. It makes it simpler to read than putting the table names all the time.
In your PHP you can then reference $row['c_uid'] or $row['p_uid'].

Union multiple table with different column for order by date

I search a solution for do this req' SQL because this don't work, like_supplier table and like product table work correctly but when i add the table comments this don't work, i know haven't the same column in table comment but how i can do this correctly ? Thanks in advance.
SELECT DISTINCT *
FROM (
(SELECT DISTINCT lp.customer_id, lp.`date`, lp.`product_id`, lp.`classes`, Null as `comment`
FROM
`like_product` as lp,
`supplier_products` as sp
WHERE
sp.`product_id` = lp.`product_id`
AND sp.`supplier_id`=".$customer_id.")
UNION DISTINCT
(SELECT DISTINCT ls.`customer_id`,ls.`date`, Null as `product_id`, ls.`classes`, Null as `comment`
FROM
`like_supplier` as ls,
`supplier_products` as sp
WHERE
sp.`supplier_id`=".$customer_id."
AND sp.`product_id` = ls.`product_id`)
UNION DISTINCT
(SELECT com.`sender_id`, com.`date`, com.`product_id`, com.`classes`, com.`comment`
FROM `comments` as com)) as a
ORDER BY a.`date` desc
Try just to clarify all aliases and column names:
SELECT DISTINCT
a.id,
a.`date`,
a.product_id,
a.classes,
a.comment
FROM (
(SELECT DISTINCT
lp.customer_id as id,
lp.`date` as `date`,
lp.`product_id` as product_id,
lp.`classes` as classes,
Null as comment
FROM
`like_product` as lp
INNER JOIN
(SELECT *
FROM
`supplier_products`
WHERE
supplier_id=".$customer_id."
) as sp
ON
sp.`product_id` = lp.`product_id`
)
UNION DISTINCT
(SELECT DISTINCT
ls.`customer_id` as id,
ls.`date` as `date`,
Null as `product_id`,
ls.`classes`,
Null as `comment`
FROM
`like_supplier` as ls
INNER JOIN
(SELECT
*
FROM
`supplier_products`
WHERE
supplier_id=".$customer_id."
) as sp
ON sp.`product_id` = ls.`product_id`)
UNION DISTINCT
(SELECT
com.`sender_id` as id,
com.`date` as `date`,
com.`product_id` as product_id,
com.`classes` as classes,
com.`comment` as comment
FROM `comments` as com
)
) as a
ORDER BY a.`date` desc

Join on specific key not working

I have four tables like this:
schoolyear (id)
student (id, schoolyear_id)
test (id)
grade (id, test_id, student_id, score)
A student is associated with a schoolyear (through fk), and a grade is associated with a test and a student through fk's.
I want all students for a particular schoolyear to be returned regardless, and join the test score for the student along with all other fields from the student table if it exists. If not, the score field should be null. Here is what I have:
SELECT *, `student`.`id` as `studentid`
FROM `student`
LEFT JOIN `grade` ON `grade`.`student_id` = `student`.`id`
WHERE `student`.`schoolyear_id` = ?
There's nothing in my current statement telling it a particular test yet, but that's what I want.
SELECT *, `student`.`id` as `studentid`, t.*
FROM `student`
LEFT JOIN `grade` ON `grade`.`student_id` = `student`.`id`
LEFT JOIN `test` t ON `t`.`id` = `grade`.`test_id`
WHERE `student`.`schoolyear_id` = ?
and test_id = ?
Just add the conditional to the ON clause. This will ensure that students stay included, even if there's no grade for that test.
SELECT g.*, s.id
FROM Student s
LEFT JOIN Grade g
ON g.student_id = s.id
AND g.id = ?
WHERE s.schoolyear_id = ?

how to inner join three tables and get count

I have three tables:
members (id, name, surname, usr_img)
user_uploads (imgID, user_id, filename, description, up_time)
img_likes (likeID, img_id, user_id)
I need to get all from user_uploads, and using the img_id, get uploader info from members and make 2 counts in the img_likes table (check if user_id (from session variable) and img_id exists and get the img total likes).
SELECT user_uploads.* AS uu, members.*, COUNT(img_id, user_id) AS usr_liked, COUNT(img_id) AS total_likes
FROM user_uploads
INNER JOIN members AS m ON m.id = uu.user_id -- owner info
INNER JOIN img_likes AS il ON il.img_id = uu.imgID AND il.user_id = ? -- check if logged in user already liked
INNER JOIN img_likes AS ilt ON ilt.img_id = uu.imgID -- total likes
GROUP BY img_id, user_id
ORDER BY up_time DESC
I don't need to get nothing from img_id, just count the number of rows. I don't know if an inner join is necessary, maybe to specify the user_id?
and to get the counts:
$usr_liked = row['usr_liked'];
$total_likes = rwo['total_likes'];
Will that work?
EDIT: add new query:
SELECT
user_uploads.*,
(
SELECT *
FROM members m
WHERE m.id = user_uploads.user_id
),
(
SELECT COUNT(*)
FROM img_likes t
WHERE t.img_id = user_uploads.imgID AND t.user_id = ?
) AS user_likes,
(
SELECT COUNT(*)
FROM img_likes t
WHERE t.img_id = user_uploads.imgID
) AS total_likes
FROM user_uploads
ORDER BY up_time DESC
You could use subquery:
SELECT
uu.*,
m.*,
(
SELECT COUNT(t.*)
FROM img_likes t
WHERE t.img_id = uu.imgID AND t.user_id = ?
) AS user_likes,
(
SELECT COUNT(t.*)
FROM img_likes t
WHERE t.img_id = uu.imgID
) AS total_likes
FROM
user_uploads AS uu
INNER JOIN members AS m ON
m.id = uu.user_id
ORDER BY
uu.up_time DESC

Categories