I wonder if someone could help me with an SQL query.
I am trying to display all results except where in Table 2 both the userid column equals 1 and the hidden column equals 1.
I am basically trying to hide results from individual users based on their id and the value in the hidden column either 1 or empty.
I have so far managed to make a query that does the opposite and can't figure out how to change it. I have tried adding !=, <>, NOT and a few other things, but nothing is working for me!
Here is the query I am working with.
$stmt = $conn->prepare("SELECT tl.id, tl.name, tl.locale, uh.hidden
FROM theList AS tl
LEFT JOIN user_hidden_list AS uh ON uh.est_id = tl.id
WHERE uh.userid = '1' AND uh.hidden = '1'");
How do I display all results expect those with a uh.userid = 1 and uh.hidden = 1
UPDATE: Tables
Table: user_hidden_list
userid | Hidden | est_id
---------------------------
1 | 1 | 1
2 | 1 | 1
1 | 1 | 2
Table: theList
id | name | locale
------------------------
1 | Jacks | LDN
2 | MacD's | LDN
3 | BK | LDN
4 | Byron | LDN
So if I am logged in with and userid of:
1 I should see: BK and Byron.
2 I should see: MacD's, BK and Byron.
3 (or anything else) I should see: Jacks, MacD's, BK and Byron.
If i understood correctly, the table user_hidden_list mantains a relation between one user and the users he can't see. So for, example, user with id equal to 1 (matching on column userid) can't see users 1 and 2 (matching on column est_id).
So, for a particular user with ID = X, we can get the ID list of user he can't see like next:
SELECT
est_id
FROM
user_hidden_list
WHERE
userid = X AND hidden = 1;
Using the previous query, we can get the visible users for user X like on next query:
SELECT
tl.id, tl.name, tl.locale
FROM
theList AS tl
WHERE
tl.id NOT IN (SELECT est_id
FROM user_hidden_list
WHERE userid = X AND hidden = 1);
I'm sure there will be a better (elegant) way to do this, but i'm just leaving work and my mind is not working nice now.
I am trying to display all results except where in Table 2 both the userid column equals 1 and the hidden column equals 1.
This does not suggest an outer join. You seem to want:
SELECT tl.id, tl.name, tl.locale, uh.hidden
FROM theList tl JOIN
user_hidden_list uh
ON uh.est_id = tl.id
WHERE NOT (uh.userid = 1 AND uh.hidden = 1);
I am guessing that userid and hidden are numbers of some sort, so I removed the single quotes. If they are really strings, then use the single quotes.
This also assumes that these values cannot be NULL. If that is a possibility, then the logic can be adjusted (using the null-safe comparator <=>).
Try out this, with even parantheses:
SELECT tl.id, tl.name, tl.locale, uh.hidden
FROM theList tl JOIN
user_hidden_list uh
ON uh.est_id = tl.id
WHERE (uh.userid <> 1 AND uh.hidden <> 1);
Related
I don't know which way would be better for PHP and SQL.
I design and program my own comment system and I would like id2=0 its a comment and id2>0 its sub-comment in one table. This means that if someone wrote a sub-comment in a comment with ID=1, then ID2 is responsible for assigning (sub-comment) to ID=1 (comment).
I have one table comments like this:
id | id2 | smt | etc.
1 | 0 | x | x //comment with sub-comment where id=3
2 | 0 | x | x //comment
3 | 1 | x | x //that is sub-comment for comment where id=1
I'm displaying this in a while loop because I need to print all the data from comments. Like this:
$sqlkom="SELECT * FROM `comments` WHERE `id`='".$row['id']."' ORDER BY id DESC LIMIT 20";
if($resultkom = mysqli_query($con, $sqlkom)){
if(mysqli_num_rows($resultkom)){
while($rowkom = mysqli_fetch_assoc($resultkom)) {
echo HtmlFormatFunction($rowkom['id'],$rowkom['id2'],$rowkom['smt'],$rowkom['etc'])
I won't achieve this: if($rowkom['id']==$rowkom['id2']) without adding another while() loop, right?
or maybe it is enough to modify the SQL query to achieve this effect? Please, help me with the right solution.
You can join your table with itself, something like that:
SELECT c.id, c.id2, c.smt, c.etc,
s.id, AS s_id, s.id2 AS s_id2, s.smt AS s_smt, s.etc AS s_etc
FROM `comments` c LEFT JOIN `comments` s
ON c.id = s.id2
WHERE c.id = ?
ORDER BY c.id DESC LIMIT 20";
Then you should get minimum one line for the first comment or as much as lines for the first comment as there are sub-comments. Then the first four fields will repeat, but thats not an issue. Maybe this is not perfect. Hope you get the idea.
The question has been resolved. But if you have a "better" or another way to do it then feel free to add a comment! Thanks all for reading! :)
I'm trying to make a dynamic query. Everything is working perfectly except for one thing. I've Google'd for days but I can't figure out how I can make the following work;
SELECT project.name, project.description, track.name, track.description
, SDG.position, SDG.title, SDG.description
, sprint_numbers.number, sprint_options.option
, resources.name, resources.description
, URLs.URL
FROM project INNER JOIN track ON project.track_id = track.id
INNER JOIN project_SDG ON project.id = project_SDG.project_id
INNER JOIN SDG ON project_SDG.SDG_id = SDG.id
INNER JOIN sprint ON sprint.project_id = project.id
INNER JOIN sprint_numbers ON sprint_numbers.id = sprint.sprint_number_id
INNER JOIN sprint_options ON sprint_options.id = sprint.sprint_option_id
INNER JOIN resources ON project.id = resources.project_id
INNER JOIN URLs ON URLs.id = resources.id
WHERE 1=1
AND MATCH (project.name) AGAINST (:name_project)
AND MATCH (project.description) AGAINST (:description_project)
AND SDG.id = :SDG_1
AND SDG.id = :SDG_2
The query executes but does not return anything. The problem is that the SDG.id can't be true to both :SDG_1 and :SDG_2.
Using the OR operator works, but that does not return it the way I want. It must "act" as an AND operator. (:SDG_1 & :SDG_2 are the names of the PHP variables that bind to the SQL statement parameters.)
The query should filter for both values. The values given to :SDG_1 and :SDG_2 must both exist in the SDG.id column of the project_SDG table. If the value of :SDG_1 exists, but :SDG_2 not, then the query should not return anything.
I found this on StackOverflow but it did not work for me: SELECTING with multiple WHERE conditions on same column
I hope someone can help me out.
EDIT: minimal reproducible example
QUERY:
SELECT * FROM project
INNER JOIN project_SDG ON project.id = project_SDG.project_id
INNER JOIN SDG ON project_SDG.SDG_id = SDG.id
WHERE SDG.id = 1 AND SDG.id = 7 AND SDG.id = 14 AND SDG.id = 17
Project table
+------------------+---------------------------+------------+
| id name | description | track_id |
+------------------+---------------------------+------------+
| 1 project name | This is a description 2 | |
+------------------+---------------------------+------------+
SDG table
+-----+-----------+-------------+---------------------------------------------+
| id | position | title | description |
+-----+-----------+-------------+---------------------------------------------+
| 1 | 1 | SDG 1 to 17 | There're multiple SDGs ranging from 1 to 17 |
| 17 | 17 | SDG 1 to 17 | There're multiple SDGs ranging from 1 to 17 |
+-----+-----------+-------------+---------------------------------------------+
project.SDG (bridge-table)
+------------+--------+
| project.id | SDG.id |
+------------+--------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
+------------+--------+
You want for each project.id both values :SDG_1 and :SDG_2 to exist for SDG.id, so use this in the WHERE clause:
WHERE 1=1
AND MATCH (project.name) AGAINST (:name_project)
AND MATCH (project.description) AGAINST (:description_project)
AND project.id IN (
SELECT project_id
FROM project_SDG
WHERE SDG_id IN (:SDG_1, :SDG_2)
GROUP BY project_id
HAVING COUNT(DISTINCT SDG_id) = 2
)
Could you provide a minimal reproducible example for your query?
Generally speaking, one field cannot be equal to two different values in the same time. So, you have either mixed up the logical operators or you need two different fields.
I can assume that in your case there may be several related records with different values. In this case, you need to join the same table twice with different aliases. Let's say as SDG1 and SDG2. After that you can compare
... `SDG1`.id = :SDG_1 AND `SDG2`.id = :SDG_2
Update:
The win trick is groupping. You can enumerate all required SDG IDs and count how many of them is in group. Just for example in case of two IDs:
SELECT project.id
FROM project
JOIN project_SDG ON project_SDG.project_id = project.id
JOIN SDG ON SDG.id = project_SDG.SDG_id
WHERE SDG.id IN(1,2)
GROUP BY project.id
HAVING COUNT(*) = 2
See my sandbox here: https://www.db-fiddle.com/f/pixe3Zcs75Mq2PyCYPk913/0
If you need all project's fields, you have to put this into sub-query as
... WHERE id IN ( subquery here )
Subquery example: https://www.db-fiddle.com/f/pixe3Zcs75Mq2PyCYPk913/1
I have already answered here, but I have another approch.
1. Find bunch of IDs assotiated with some project
To find project IDs we can test lonely pivot table without any join:
SELECT project_id FROM project_SDG
WHERE SDG_id IN(1,2,6)
GROUP BY project_id HAVING COUNT(*) = 3
it gives us list of Project IDs
2. Access all project fields and add extra conditions
SELECT project.*
FROM project
JOIN (
SELECT project_id FROM project_SDG
WHERE SDG_id IN(1,2,6)
GROUP BY project_id HAVING COUNT(*) = 3
) AS ids ON ids.project_id = project.id
WHERE
MATCH(project.name) AGAINST ('project') AND
MATCH(project.description) AGAINST ('sit')
you can play with it here: https://www.db-fiddle.com/f/pixe3Zcs75Mq2PyCYPk913/3
3. Prepare query on the PHP side
I will use known technique to prepare SQL statement.
$ids = [1, 2, 6]; // it can come from request parameters
$text1 = 'project';
$text2 = 'sit';
// build ?,?,?,... pattern
$qmarks = implode(',', array_fill(0, count($ids), '?'));
// Use SQL query above
$sth = $dbh->prepare("
SELECT project.*
FROM project
JOIN (
SELECT project_id FROM project_SDG
WHERE SDG_id IN({$qmarks})
GROUP BY project_id HAVING COUNT(*) = ?
) AS ids ON ids.project_id = project.id
WHERE
MATCH(project.name) AGAINST (?) AND
MATCH(project.description) AGAINST (?)
");
$sth->execute(array_merge($ids, [count($ids), $text1, $text2]));
$records = $sth->fetchAll();
I have 2 tables about blood bank:
donates
orders
in donates table I have 2 fields showing how many donations we have:
------------------------
| blood_group | amount |
------------------------
| A+ | 2 |
| B- | 3 |
| O+ | 4 |
| A+ | 3 |
| O+ | 1 |
in orders table I have 2 column that how many requests we submit based on blood group:
------------------------
| blood_group | amount |
------------------------
| A+ | 4 |
| B- | 3 |
| O+ | 4 |
| AB- | 6 |
My problem is I want to use mysqli query to get an array that show me this result based on these conditions:
show how many we need group by blood_group
if we don't need any blood_group or we don't have any request for that blood type show zero (not showing null)
not showing negative number for our blood shortage
I manage to do this so far:
<?php
$con = mysqli_connect("localhost", "root", "", "test");
// Check connection
if (mysqli_connect_errno()) {
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
$sql ="SELECT donates.blood_group as blood_group,
donates.amount as donates_amount,
orders.amount as orders_amount,
FROM `donates`
LEFT JOIN `orders`
ON donates.blood_group = orders.blood_group
GROUP BY donates.blood_group";
// Perform queries
$result = mysqli_query($con, $sql);
if (!$result = mysqli_query($con, $sql)) {
echo "SQLSTATE error: " . mysqli_sqlstate($con);
echo "<br>";
echo "SQLSTATE error: " . mysqli_error($con);
exit;
}
$result = mysqli_fetch_all($result, MYSQLI_ASSOC);
var_dump($result);
mysqli_close($con);
That query shows me sum of blood_groups but here is the main question:
So here are the main questions:
how to subtract (donates_amount and orders_amount)
how to make them positive (subtract which one first)
how to show the result even if one blood group is not presented on the other (full join)
Use union all and group by:
select blood_group, sum(donate_amount) as donate_amount,
sum(order_amount) as order_amount
from ((select blood_group, amount as donate_amount, 0 as order_amount
from donates
) union all
(select blood_group, 0 as donate_amount, amount as order_amount
from orders
)
) od
group by blood_group;
The only caveat is that a blood group needs to be in one of the tables. If you have a separate table of all of them, you should use that. For instance:
select bg.*,
coalesce(donate_amount, 0) as donate_amount,
coalesce(order_amount, 0) as order_amount
from blood_groups bg left join
(select blood_group, sum(amount) as donate_amount
from donates
group by blood_group
) d
on d.blood_group = bg.blood_group left join
(select blood_group, sum(amount) as order_amount
from donates
group by blood_group
) o
on o.blood_group = bg.blood_group ;
In either of these queries, you can get the difference using - and show negative numbers as 0 using greatest(). For instance:
greatest(sum(donate_amount) - sum(order_amount), 0)
To answer your first question :
how to subtract (donates_amount and orders_amount)
You must use SUM() function with a minus sign:
SUM(donates.amount - orders.amount);
this will subtract the total sum of two tables
But we have some problem here: you may have null values (because you may not have some of the blood groups present in one of tables) that give the wrong result. you must change the null values to zero with COALESCE() function:
SUM(COALESCE(donates.amount,0) - COALESCE(orders.amount,0))
We must extra check if the result does not equal to null:
COALESCE(SUM(COALESCE(donates.amount,0) - COALESCE(orders.amount,0)),0)
how to make them positive (subtract which one first)
And at last if you want to avoid negative numbers you must use mysqli math functions named ABS() that give you absulute value:
ABS(COALESCE(SUM(COALESCE(donates.amount,0) - COALESCE(orders.amount,0)),0))
so your query will look like this:
$sql = "SELECT donates.blood_group as blood_group,
COALESCE(donates.amount,0) as donates_amount,
COALESCE(orders.amount,0) as orders_amount,
ABS(COALESCE(SUM(COALESCE(donates.amount,0) - COALESCE(orders.amount,0)),0)) as needed_amount
FROM `donates`
LEFT JOIN `orders`
ON donates.blood_group = orders.blood_group
GROUP BY donates.blood_group";
how to show the result even if one blood group is not presented on
the other (full join)
In order to make full join you must use union with the invers form of your query. so that you find other records in orders table and unite the results into one results:
$sql = "SELECT donates.blood_group as blood_group,
COALESCE(donates.amount,0) as donates_amount,
COALESCE(orders.amount,0) as orders_amount,
ABS(COALESCE(SUM(COALESCE(donates.amount,0) - COALESCE(orders.amount,0)),0)) as needed_amount
FROM `donates`
LEFT JOIN `orders`
ON donates.blood_group = orders.blood_group
GROUP BY donates.blood_group
UNION
SELECT orders.blood_group as blood_group,
COALESCE(donates.amount,0) as donates_amount,
COALESCE(orders.amount,0) as orders_amount,
ABS(COALESCE(SUM(COALESCE(orders.amount,0) - COALESCE(donates.amount,0)),0)) as needed_amount
FROM `orders`
LEFT JOIN `donates`
ON orders.blood_group = donates.blood_group
GROUP BY orders.blood_group";
I have these two tables - user_schedules and user_schedule_meta, shown below:
------------------------------------
| id | scheduler_id | status |
------------------------------------
1 3 pending
2 5 active
3 6 active
and
----------------------------------------------
| id | user_schedule_id | meta_key |meta_value
----------------------------------------------
1 3 course-id 135
2 3 session-id 15
3 3 schedule-id 120
I want to write a query to enable me select, for example, from both tables where EVERYONE of the below 5 conditions are met:
user_schedule_id = 3
scheduler_id = 6
session_id = 15
course-id = 135
schedule-id = 120
This is what I have so far, but it is not working:
SELECT user_schedule_meta.`id` FROM user_schedule_meta, user_schedules
WHERE user_schedules.`scheduler_id` = 6
AND user_schedules.id = user_schedule_meta.`user_schedule_id`
AND (
(user_schedule_meta.`meta_key` = 'course-id' AND user_schedule_meta.`meta_value` = 135)
OR (user_schedule_meta.`meta_key` = 'session-id' AND user_schedule_meta.`meta_value` = 15)
OR (user_schedule_meta.`meta_key` = 'daily-schedule-id' AND user_schedule_meta.`meta_value` = 120)
)
GROUP BY user_schedule_meta.`id`
Any suggestions what I am not doing right?
This is a typical key-value store lookup problem. These are trickier than they look in SQL, in that they require multiple JOIN operations.
You need a virtual table with one row per user_schedules.id value, then you can filter it. So
SELECT u.id, u.scheduler_id
FROM user_schedules u
JOIN user_schedule_meta a ON u.id=a.user_schedule_id AND a.meta_key='course-id'
JOIN user_schedule_meta b ON u.id=b.user_schedule_id AND b.meta_key='session-id'
JOIN user_schedule_meta c ON u.id=c.user_schedule_id AND c.meta_key='daily-schedule-id'
WHERE a.meta_value = 135 -- value associated with course-id
AND b.meta_value=15 -- value associated with session-id
AND c.meta_value=120 -- value associated with daily-schedule-id
Notice also that you can list your table with associated attributes like this. This trick of joining the key/value table multiple times is a kind of pivot operation. I use LEFT JOIN because it will allow the result set to show rows where an attribute is missing.
SELECT u.id, u.scheduler_id, u.status,
a.meta_value AS course_id,
b.meta_value AS session_id,
c.meta_value AS daily_schedule_id
FROM user_schedules u
LEFT JOIN user_schedule_meta a ON u.id=a.user_schedule_id AND a.meta_key='course-id'
LEFT JOIN user_schedule_meta b ON u.id=b.user_schedule_id AND b.meta_key='session-id'
LEFT JOIN user_schedule_meta c ON u.id=c.user_schedule_id AND c.meta_key='daily-schedule-id'
try this is code
select * from user_schedule_meta where user_schedule_id=3 and
(meta_key='session-id' AND meta_value=15
or meta_key='daily-schedule-id' AND meta_value=120
or meta_key='course-id' AND meta_value=135
)
I have three table Like this:
members_tbl
id | Fullname | Email | MobileNo
attendance_in_tbl
id | member_id | DateTimeIN
attendance_out_tbl
id | member_id | DateTime_OUT
I want to select all members for date: 2014-03-10 by this query:
SELECT
attendance_in.EDatetime,
members_info.mfullname,
attendance_out.ODatetime
FROM
attendance_in
LEFT JOIN members_info ON members_info.id = attendance_in.MemID
LEFT JOIN attendance_out ON attendance_out.MemID = attendance_in.MemID
WHERE date(attendance_in.EDatetime) OR date(attendance_out.ODatetime) = "2014-03-10"
But it give me different results in Attendace_out Results
You have a mistake in your query.
You wrote:
WHERE date(attendance_in.EDatetime) /* wrong! */
OR date(attendance_out.ODatetime) = "2014-03-10"
This is wrong, as the first expression date(attendance_in.EDatetime) always evaluates to true.
You may want
WHERE date(attendance_in.EDatetime) = "2014-03-10"
OR date(attendance_out.ODatetime) = "2014-03-10"
But, this is guaranteed to perform poorly when your attendance_in and attendance_out tables get large, because it will have to scan them; it can't use an index.
You may find that it performs better to write this:
WHERE (attendance_in.EDatetime >='2014-03-10' AND
attendance_in.EDatetime < '2014-03-10' + INTERVAL 1 DAY)
OR (attendance_out.EDatetime >='2014-03-10' AND
attendance_out.EDatetime < '2014-03-10' + INTERVAL 1 DAY)
That will check whether either the checkin our checkout time occurs on the day in question.