NOT EXISTS not considered in the results - php

I'm trying to get the results from 'comments' table with the exception of the ones in the 'readsbaby' table.
The result i'm getting is from comments but no effect of the NOT EXISTS statement, so the result is all the comments.
Both tables have common data that should not be included in the result.
I checked the data and the syntax many times.
Still this query will return all comments without taking in consideration the AND NOT EXISTS close.
public function get_user_comments($post_id)
{
$user_id = $this->session->userdata('id');
$group_id = $this->session->userdata('group_id');
$sql = "SELECT *
FROM comments
WHERE DATE(created_on) > DATE_SUB(CURDATE(), INTERVAL 1 DAY)
AND comments.group_id = " . $group_id . "
AND comments.user_id != " . $user_id . "
AND NOT EXISTS ( SELECT *
FROM readsbaby
WHERE comments.id = readsbaby.notification_id
AND comments.group_id = readsbaby.group_id
AND readsbaby.user_id = " . $this->session->userdata('id') . "
AND comments.nature1 = readsbaby.notification_type
) ";
return $data=$this->db->query($sql)->result_array();
}
Expecting to get the result filtered by the NOT EXISTS close.

I'd change the query this way, to have only the rows without any match in the table readsbaby:
public function get_user_comments($post_id)
{
$user_id = $this->session->userdata('id');
$group_id = $this->session->userdata('group_id');
$sql = "SELECT *
FROM comments
LEFT OUTER JOIN readsbaby ON comments.id = readsbaby.notification_id
AND comments.group_id = readsbaby.group_id
AND readsbaby.user_id = " . $this->session->userdata('id') . "
AND comments.nature1 = readsbaby.notification_type
WHERE DATE(created_on) > DATE_SUB(CURDATE(), INTERVAL 1 DAY)
AND comments.group_id = " . $group_id . "
AND comments.user_id != " . $user_id . "
AND ISNULL(readsbaby.notification_id )";
return $data=$this->db->query($sql)->result_array();
}

This is easier to accomplish using the correct mysql JOIN.
From your post, I have gathered that you want to retrieve all the values from comments where they don't exist in readsbaby. The id column in comments associates with the notification_id in readsbaby for any 'matching' (duplicate) entries.
On this assumption, you can do this at a simplistic level like so
SELECT c.* FROM comments c
LEFT JOIN readsbaby r ON c.id = r.id WHERE r.notification_id IS NULL;
...which should translate to your context as something like:
SELECT c.* FROM comments c
LEFT JOIN readsbaby r ON c.id = r.notification_id
WHERE
r.notification_id IS NULL
AND DATE(c.created_on) > DATE_SUB(CURDATE(), INTERVAL 1 DAY)
AND c.group_id = " . $group_id . "
AND c.user_id != " . $user_id . "
If, I have this wrong and you're looking to only get the data that isn't present in both, you'd need to use something like:
SELECT c.* FROM comments c
LEFT JOIN readsbaby r ON c.id = r.notification_id
WHERE
r.notification_id IS NULL
AND DATE(c.created_on) > DATE_SUB(CURDATE(), INTERVAL 1 DAY)
AND c.group_id = " . $group_id . "
AND c.user_id != " . $user_id . "
UNION
SELECT r.* FROM readsbaby r
LEFT JOIN comments c ON c.id = r.notification_id
WHERE
c.id IS NULL
AND r.group_id = " . $group_id . "
AND r.user_id != " . $user_id . "
This may need tweaking to suit, as there's very little to explain the data and table relationship.
Note: Do not inject SQL as you've done. Consider parameterising your query with PDO or any other prepared statements (e.g. http://php.net/manual/en/pdo.prepare.php)

Related

Limit returned MySQL results from PHP query

I am trying to fix an issue with our website and I'm afraid it is a bit out of my depth at the moment. We have a social network website and users can search for friends through a friend section.
The problem is that when you open that section the SELECT query seems to run a search for ALL users on the site. And we have over 15,000 users so the page is incredibly slow and normally times out.
I was able to limit the displayed result to 15 users per page, but the query is not well optimized and apparently selecting all users.
Here is the SELECT query code we have at the moment:
$sql_sel_friend_req = "SELECT m.*, mp.iMAgeGroup as modelAgeGroup,
c.iMAgeGroup as clientAgeGroup, mo.iModelId, mp.vImage as modelImage,
cc.vImage as clientImage FROM member m
left join model as mo on (mo.iModelId = m.MemberId && m.MemberType = 'Model')
left join model_profile as mp on (mp.iModelId = mo.iModelId)
left join client as c on (c.iClientId = m.MemberId && m.MemberType = 'Client')
left join client_company as cc on (cc.iClientId = c.iClientId) WHERE m.MemberId != " . $_SESSION['sess_Login_Id'] . " && m.MemberStatus = 'Active' &&
m.MemberId not in (SELECT distinct(mf.iFriendMemberId1) from member_friends mf where mf.iFriendMemberId2 = " . $_SESSION['sess_Login_Id'] . " && mf.eFriendMemberType2 = '" . $_SESSION['sess_Login_Type'] . "') &&
m.MemberId not in (SELECT distinct(mf1.iFriendMemberId2) from member_friends mf1 where mf1.iFriendMemberId1 = " . $_SESSION['sess_Login_Id'] . " && mf1.eFriendMemberType1 = '" . $_SESSION['sess_Login_Type'] . "') ";
I've tried to use Limit and Offset but this only caused SQL errors.
If you have any ideas please let me know.
Thank you

left join outputs incorrect first block of data

I'm trying to get data from two tables: users and posts using left join.
$items = '';
$sql = "
SELECT u.id as uid
, u.name as uname
, p.id as pid
, p.date as pdate
, p.title as ptitle
, p.user as puser
FROM users u
LEFT
JOIN posts p
ON u.id = p.user
WHERE u.role = 'mod'
GROUP
BY p.title;";
$stmt = $db->query($sql);
while($row = $stmt->fetch()){
$date = strtotime($row['pdate']);
$date = date("d-m-Y", $date);
$items.= "<div class='itemp' data-id=" . $row['pid'] . " data-user='" . $row['uname'] . "'>" .
"<span class='spandate'>" . $date . "</span>" .
"<span class='spantitle'>" . $row['ptitle'] . "</span>" .
"<span class='spanuser'>" . $row['uname'] . "</span>" .
"</div>\n";
}
echo $items;
Output is ok except first row where I see the date - 1.1.1970 - but there is no any row in posts with that date. Plus - in the same row ptitle is missing.
Also, is there a better way to create this query, avoiding as keyword ?
desired output (single block):
<div class='itemp' data-id=116 data-user='JOHN SMITH'><span class='spandate'>01-12-2017</span><span class='spantitle'>BLUE SKY</span><span class='spanuser'>JOHN SMITH</span></div>
Also, is there a better way to create this query, avoiding as keyword ?
Yes (but not better in my opinion):
SELECT users.id,
users.name,
posts.id,
posts.date,
posts.title,
posts.user
FROM users
LEFT
JOIN posts
ON users.id = posts.user
WHERE users.role = 'mod'
GROUP
BY posts.title;
But as mentioned in the comments you have to modify your while loop in this case to get access to your values. But I think this is not a better way of writing this query cause it makes it harder to read.
Regarding your JOIN problem. this depends on what your result should be (I guess you only want users with posts ). So you should take a look at INNER JOIN instead of LEFT JOIN. You will get only results which also have posts entrys.
Due to the nature of LEFT JOIN all entrys on the left table (in your case users) will be used, even if they donĀ“t have an entry in the posts table.
INNER JOIN will only return results which match results in both tables.

Need to add filters to a complex query

i am trying to write a query which will help me recover class, batch, and institution details from db
so far i have managed to make the following work:
SELECT
gallery.path_url AS insimage,
r3.*
FROM
gallery
INNER JOIN (
SELECT
institution.id AS insid,
institution.ProfileImageID,
institution.`name` AS insname,
institution.Locality AS inslocality,
institution.RegistrationFee AS ins_reg_fee,
institution.IsTrail AS ins_is_trail,
institution.LatLong AS latlong,
r2., activity.`name` AS activityname
FROM
institution
INNER JOIN (
SELECT
class., count() AS batch_count,
r1.
FROM
class
INNER JOIN (
SELECT
batch.ID AS batch_id,
batch.ClassID AS class_id,
batch.LevelID AS level_id,
batch.agegroupID AS agegroup_id,
count(*) AS num_batches,
min(Price) AS min_price,
max(Price) AS max_price
FROM
batch,
batchstarttimes
WHERE
batch.ID = batchstarttimes.BatchID
AND batchstarttimes.StartTime BETWEEN '" . $sttime . "'
AND '" . $endtime . "'
GROUP BY
batch.ID
) AS r1
WHERE
r1.class_id = class.ID
GROUP BY
r1.class_id
) AS r2 ON r2.institutionid = institution.id
INNER JOIN activity ON activity.id = r2.activityid
WHERE
activity.`name` LIKE '%" . $searchterm . "%'
AND institution.Locality LIKE '%" . $locality . "%'
AND institution.StatusID = 1
AND level_id = 1
AND agegroup_id = 5
) AS r3 ON r3.ProfileImageID = gallery.ID
I am stuck when i try to add two more filters batch.LevelID and batch.agegroupID
any way i can get help or someone point me to a sql visualizr tool will be helpful.
In your WHERE clause, replace:
AND level_id = 1
AND agegroup_id = 5
with:
AND batch.LevelID = CASE WHEN " . $levelID . " = 0 THEN batch.LevelID ELSE " . $levelID . " END
AND batch.agegroupID = CASE WHEN " . $ageGroupID . " = 0 THEN batch.agegroupID ELSE " . $ageGroupID . " END
The point is to compare batch.LevelID and batch.agegroupID against themselves if you want to ignore them for the filtering (this way they'll always be true) or compare them against $levelID and $ageGroupID values for specific rows.
Also, it's not recommended to use the variables directly in the query, as it's vulnerable to SQL injections attacks. Read about prepared statements here.

How would I add a date range query in my current MySQL Query?

I have a question how do I add a query for a date range here in my MySQL query. The date is generated only.
Here is my Query:
SELECT TS.id AS TSid, employeeid, `date` AS date_d, TS.TI1 AS TI1, TS.TO1 AS TO1,
CONCAT(E.lastName,', ',E.firstName,' ',E.middleInitial,'.') AS 'fullname',
(TIME_TO_SEC(CAST(TS.`TI1` AS time))/3600) AS 'timeinint',
(TIME_TO_SEC(CAST(TS.`TO1` AS time))/3600) AS 'timeoutint',
E.startDate, C.companyName, B.branchName,
R.mon, R.tue, R.wed, R.thu, R.fri, R.sat, R.sun
FROM timeschedule AS TS
LEFT JOIN employee AS E ON TS.employeeid = E.id
LEFT JOIN company AS C ON E.companyid = C.id
LEFT JOIN branch AS B ON E.branchid = B.id
LEFT JOIN restday AS R ON R.id = E.restDayid
WHERE TS.`show` = '1'
ORDER BY employeeid ASC
HEre is how I get the input:
$mfrom = $this->input->get('mfroma'); //From: Month
$yfrom = $this->input->get('yfroma'); //From: Year
$yto = $this->input->get('ytoa'); //To : Year
With the fields you've specified (from month and year and to year) you can add these conditions into your WHERE clause. I'm going to use the E.startDate field for this example:
$sql = "-- your query here...
WHERE
TS.`show` = '1'
AND MONTH(E.startDate) >= " . $mfrom . "
AND YEAR(E.startDate) BETWEEN " . $yfrom . " AND " . $yto . "
ORDER BY employeeid ASC";
Docs:
- MONTH()
- YEAR()
Note: PHP parses variables inside double quotes, but I personally prefer to keep PHP variables outside of them.

MySQL - Datas all included in larger data set

So I have 2 tables:
user_selection (id, user_id, fiche_id)
fiche (id, title, ...)
(fiche = sheet)
I have a research request which gives me some fiche ids
SELECT fiche.*, IF(fiche.fiche_type_id = 2,fiche.instance,fiche_type_texte.type_texte) AS type_texte, IF(fiche.fiche_type_id = 2,CONCAT(fiche.affaire," - ", fiche.titre),fiche.titre) AS titre, IF(fiche.fiche_type_id = 1,fiche.date_publication,fiche.date_texte) AS date_publication
FROM enviroveille_bascule.fiche
LEFT JOIN enviroveille_bascule.fiche_type_texte ON(fiche.fiche_type_texte_id = fiche_type_texte.id)
LEFT JOIN enviroveille_bascule.fiche_x_keyword ON(fiche_x_keyword.fiche_id = fiche.id)
LEFT JOIN enviroveille_bascule.fiche_x_theme ON(fiche_x_theme.fiche_id = fiche.id)
LEFT JOIN enviroveille_bascule.fiche_echeance ON(fiche_echeance.fiche_id = fiche.id)
LEFT JOIN enviroveille_bascule.fiche_x_activite ON(fiche_x_activite.fiche_id = fiche.id) LEFT JOIN enviroveille_bascule.fiche_x_nomenclature ON(fiche_x_nomenclature.fiche_id = fiche.id)
WHERE 1 = 1
AND fiche_echeance.fiche_echeance_type_id IN(2)
GROUP BY fiche.id
I'd like to know if all results from my research query is in the user selection.
So I tried :
// fiche_ids is an array of fiche.id resulting from research request
SELECT COUNT(*) AS nb
FROM " . $this->bdd . $this->table_user_selection . "
WHERE user_id = '" . $user->datas_user['id'] . "'
AND fiche_id IN ('" . implode("','", $fiche_ids) . "')
if($ds['nb'] == count($fiche_ids) && count($fiche_ids) > 0) {
return true;
} else {
return false;
}
That works well
Problem is that I have some research request giving 10K+ results.
And I have to do it several time on the same page, which makes server lags.
Is there a easy way to know if all my results are in my selection?
Note that selection may contain more fiche_id than the research result.
The best would be to be able do this in one SQL request.

Categories