LEFT JOIN with exceptions - mysql

Here's my current query:
$q = "SELECT u.uid,CONCAT_WS(' ', u.first_name, u.last_name) name, CASE WHEN t.name IS NULL THEN 0 ELSE t.name END AS teamname ";
$q .= "FROM {$users} ";
$q .= "LEFT JOIN {$schedule} ON u.uid = s.uid ";
$q .= "LEFT JOIN {$products} ON p.admitting_id = u.uid ";
$q .= "LEFT JOIN {$teams} ON t.id = s.team ";
$q .= "WHERE (u.uid = {$current_user_id} AND u.roles = 'hoa') OR ";
$q .= "(p.id={$pid} AND p.admitting_id != 0) OR ";
$q .= "(t.admitting = 1 AND s.inactive != 1 AND s.role = 'hoa' AND (s.date = '{$today}' OR (s.date = '{$yesterday}' AND t.overnight = 1))) ";
$q .= "ORDER BY u.last_name,u.first_name,t.name";
What I need to get is:
Current User, if Current User has role hoa
User where uid matches admitting_id from products table
All users with role hoa who are scheduled today or were scheduled yesterday on an overnight shift. With those scheduled users, I need the names of the team they were/are scheduled with. Duplicate uids is good because I need all the different team names if they were scheduled on multiple teams.
What I don't need to get is:
Team name for options 1 and 2 above.
What I'm getting is:
Everything perfect for #3 above.
All possible team names for #1 and #2 above, whereas I want the team names to be 0 if they're not on the schedule.
I could split this up into three queries but that's not preferable.

Sometimes formatting the query out a bit helps visualise. It doesn't need to become three queries, I agree.
Can you provide some sample schema and data, ideally both in the question description and on something like sqlfiddle for us to play with?
It's hard to visualise your structure and data, but I'm speculating the issue is every record in users table is joining to schedule with the on clause you've used.
You could move the logic of condition 3 into the on clause (it's a left join, so nothing else will break) or duplicate it in the CASE statement in the SELECT:
$q = <<<SQL
SELECT
u.uid,
CONCAT_WS(' ', u.first_name, u.last_name) name,
IF(
WHEN t.name IS NULL THEN
0
ELSE
t.name
END AS teamname
FROM {$users} AS u
LEFT JOIN {$schedule} AS s ON (u.uid = s.uid)
LEFT JOIN {$products} AS p ON (p.admitting_id = u.uid)
LEFT JOIN {$teams} AS t ON
(
t.id = s.team AND
t.admitting = 1 AND
s.inactive != 1 AND
s.role = 'hoa' AND
(
s.date = '{$today}'
OR
(
s.date = '{$yesterday}' AND
t.overnight = 1
)
)
)
WHERE
( -- 1: Current User, if Current User has role hoa
u.uid = {$current_user_id} AND
u.roles = 'hoa'
)
OR
( -- 2: User where uid matches admitting_id from products table
p.id={$pid} AND
p.admitting_id != 0
)
OR
t.name IS NOT NULL
ORDER BY
u.last_name,
u.first_name,
t.name
SQL;
I'm absolutely not certain that'll fix your problem without something real to play with... Happy to continue looking if you can provide a working example sandpit.

Related

How do I take the value from one query and use it as the id for another?

I'm trying to amend an existing table that is being populated with membership data. I've duplicate some of the code and amended it to successfully display a Members Team ID (Part 1 below). I've then duplicated it again and can successfully include the Team Expiry (Part 2) by manually adding the post meta id (in the example '105271').
I somehow need to replace the manual inserted post id in part 2 with the value that I'm getting from part 1.
Any help or guidance would be appreciated.
Part 1
elseif($table == 'memberships_team'){
$query_joins .= " LEFT JOIN (
SELECT MIN(CAST(meta_value AS UNSIGNED)) AS member_team, $wpdb->posts.post_author AS user_id FROM $wpdb->postmeta
INNER JOIN $wpdb->posts ON $wpdb->postmeta.post_id = $wpdb->posts.ID
WHERE $wpdb->postmeta.meta_key = '_team_id'
GROUP BY $wpdb->posts.post_author
) AS memberships_team ON $wpdb->users.ID = memberships_team.user_id";
}
Part 2
elseif($table == 'memberships_team_end'){
$query_joins .= " LEFT JOIN (
SELECT MIN(CAST(meta_value AS DATETIME)) AS member_team_end, $wpdb->posts.post_author AS user_id FROM $wpdb->postmeta
INNER JOIN $wpdb->posts ON $wpdb->postmeta.post_id = '105271'
WHERE $wpdb->postmeta.meta_key = '_membership_end_date'
GROUP BY $wpdb->posts.post_author
) AS memberships_team_end ON $wpdb->users.ID = memberships_team_end.user_id";
}
I hope part 1 is already executed to get controlled over part 2 .
It means memberships_team has already been processed and the member_team column has been added to the query results.
then Part 2 can be modified and it's required any intermediate variable as i commented.
elseif($table == 'memberships_team_end'){
$query_joins .= " LEFT JOIN (
SELECT MIN(CAST(meta_value AS DATETIME)) AS member_team_end, $wpdb->posts.post_author AS user_id FROM $wpdb->postmeta
INNER JOIN $wpdb->posts ON $wpdb->postmeta.post_id = memberships_team.member_team
WHERE $wpdb->postmeta.meta_key = '_membership_end_date'
GROUP BY $wpdb->posts.post_author
) AS memberships_team_end ON $wpdb->users.ID = memberships_team_end.user_id";
}
utilising member_team column obtained in memberships_team part 1.

How to view data based on user in codeigniter

I want to view data from mysql in codeigniter based on user who is logged in
but I got an error "A Database Error Occurred". I think I wrote the wrong code
t.USER = $this->session->userdata('user_id');
Here's the code:
function get_rekomen()
{
$query = $this->db->query("SELECT
c.*,
t.produk_id,
t.id_transdet,
t.kategori_id,
t.total_qty,
t.USER
FROM
transaksi_detail AS t
LEFT JOIN
(
SELECT
g.id_produk,
p.slug_produk,
p.foto,
p.foto_type,
p.harga_diskon,
p.diskon,
p.harga_normal,
p.judul_produk,
g.kat_id,
k.judul_kategori
FROM
(
SELECT
MAX(m.id_produk) AS id_produk,
m.kat_id
FROM
produk AS m
GROUP BY
m.kat_id
)
AS g
INNER JOIN
produk AS p
ON p.id_produk = g.id_produk
LEFT JOIN
kategori AS k
ON k.id_kategori = g.kat_id
)
AS c
ON c.kat_id = t.kategori_id
WHERE
t.USER = $ this -> session -> userdata('user_id');
<<// i think here's the problem
ORDER BY
total_qty DESC limit 1")->result();
return $query;
}
and the question is what is the correct code?
Here's one solution, but first make sure on your controller the $this->load->library('session'); is loaded or you can just add in on autoload.php.
Also you must add parameter to your model that will handle the user_id in session, so for example here's your model looks like:
function get_rekomen($user_id) { ...your query here... }
Then on condition using the user_id will be
t.USER = $user_id;
So on your controller, you can call your model like:
$user_id = $this->session->userdata('user_id');
$this->ModelClass->get_rekomen($user_id);
Additional: On your login phase, if the user success from logging in, the you must set the user data on session such as this $this->session->set_userdata('user_data', $session_data);
Hope this helps!
Maybe you can use this. You need to define $user_id first before adding them to WHERE statement...
$user_id = $this->session->userdata('user_id');
$sql = "SELECT c.*, t.produk_id, t.id_transdet, t.kategori_id, t.total_qty, t.USER
FROM transaksi_detail AS t
LEFT JOIN(SELECT g.id_produk, p.slug_produk, p.foto, p.foto_type, p.harga_diskon, p.diskon, p.harga_normal, p.judul_produk, g.kat_id, k.judul_kategori
FROM(SELECT MAX(m.id_produk) AS id_produk, m.kat_id
FROM produk AS m
GROUP BY m.kat_id)
AS g
INNER JOIN produk AS p ON p.id_produk = g.id_produk
LEFT JOIN kategori AS k ON k.id_kategori = g.kat_id)
AS c ON c.kat_id = t.kategori_id
WHERE t.USER = '$user_id'
ORDER BY total_qty DESC limit 1";
$query = $this->db->query($sql);
return $query->result();
Hope this can help you...

Drupal 7 DB API Query with Condition db_or etc

I'm having a little issue with getting an sql query to work in drupal 7.
Basically, I'd like to know if my drupal code for the db_api is correct for the SQL query that I expect it to generate.
Please see code below:
/*
SQL Query
Note: The the 'a' in '%a%', is just a sample of some text input by a user.
*/
SELECT u.uid, fn.field_first_name_value, ln.field_last_name_value
FROM role r
JOIN users_roles ur ON (r.rid = ur.rid)
JOIN users u ON (u.uid = ur.uid)
JOIN field_data_field_first_name fn ON (fn.entity_id = u.uid)
JOIN field_data_field_last_name ln ON (ln.entity_id = u.uid)
WHERE (fn.field_first_name_value LIKE '%a%' OR ln.field_last_name_value LIKE '%a')
AND r.name = 'custom_role'
LIMIT 0, 5
/*
DRUPAL 7 DB API CODE
*/
$name = $_POST['name'];
$res = db_select('role', 'r');
$res->join('users_roles', 'ur', 'ur.rid = r.rid');
$res->join('users', 'u', 'u.uid = ur.uid');
$res->join('field_data_field_first_name', 'fn', 'fn.entity_id = u.uid');
$res->join('field_data_field_last_name', 'ln', 'ln.entity_id = u.uid');
$res->fields('u', array('uid'));
$res->fields('fn', array('field_first_name_value'));
$res->fields('ln', array('field_last_name_value'));
$or = db_or()->condition('fn.field_first_name_value', '%'.db_like($name).'%', 'LIKE');
$or->condition('ln.field_first_name_value', '%'.db_like($name).'%', 'LIKE');
$res->condition($or)->condition('r.name', 'custom_role', '=');
$res->range(0,5);
$res->execute();
Also, if there is way that I can see the generated sql by the db api, that would be great for debugging.
Thanks.
I cannot execute it because i do not have the same tables like you but your generated query looks good.
SELECT u.uid AS uid, fn.field_first_name_value AS field_first_name_value, ln.field_last_name_value AS field_last_name_value
FROM
{role} r
INNER JOIN {users_roles} ur ON ur.rid = r.rid
INNER JOIN {users} u ON u.uid = ur.uid
INNER JOIN {field_data_field_first_name} fn ON fn.entity_id = u.uid
INNER JOIN {field_data_field_last_name} ln ON ln.entity_id = u.uid
WHERE ( (fn.field_first_name_value LIKE :db_condition_placeholder_0 ESCAPE '\\') OR (ln.field_first_name_value LIKE :db_condition_placeholder_1 ESCAPE '\\') )AND (r.name = :db_condition_placeholder_2)
LIMIT 5 OFFSET 0
If you want to print the query yourself you can use
print $res->__toString() . "\n\n";
var_export($res->getArguments());
Or
watchdog('MY_QUERY_DEBUG', $res->__toString(), $res->getArguments());
To add it to your watchdog log.

SUM() on all rows of a LEFT JOIN?

I've been playing with LEFT JOINs and I'm wondering if it's possible to get the SUM of all the ratings so far from all users in the below query. The below get's me the information on if the logged in user has rated or not, but i want to show total ratings from other users also.
$query = mysql_query("
SELECT com.comment, com.comment_id, r.rate_up, r.rate_down
FROM comments com
LEFT JOIN ratings r
ON com.comment_id = r.comment_id
AND r.user_id = '" . $user_id_var . "'
WHERE page_id = '" . $category_id_var. "'");
I've tried the following but i only get one comment/row returned for some reason.
$query = mysql_query("
SELECT com.comment, com.comment_id,
r.rate_up, r.rate_down
SUM(r.rate_up) AS total_up_ratings,
SUM(r.rate_down) AS total_down_ratings,
FROM comments com
LEFT JOIN ratings r
ON com.comment_id = r.comment_id
AND r.user_id = '" . $user_id_var . "'
WHERE page_id = '" . $category_id_var. "'");
Any help appreciated. Do i need a different kind of JOIN?
Have you tried using GROUP BY page_id on the end of your SQL?
You could do it something like this:
SELECT
com.comment,
com.comment_id,
Total.total_down_ratings,
Total.total_up_ratings
FROM comments com
LEFT JOIN
(
SELECT
SUM(r.rate_up) AS total_up_ratings,
SUM(r.rate_down) AS total_down_ratings,
r.comment_id
FROM
ratings r
GROUP BY
r.comment_id
) AS Total
ON com.comment_id = Total.comment_id
AND r.user_id = '" . $user_id_var . "'
WHERE page_id = '" . $category_id_var. "'"
If you use an aggregation function in SQL (like SUM()) you will need a corresponding GROUP BY clause.
In your case the most likely one would be com.comment_id; this will give you the sum of all ratings per comment_id:
I don't know if you are duplicating comment rows for purpose but if not you can try write subselect for ratings like this:
select comment_id, user_id, sum(rate_up) as sum_rate_up,
sum(rate_down) as sum_rate_down from ratings
group_by comment_id, user_id;
and then include it in your join query:
select com.comment, com.comment_id, r.user_id, r.sum_rate_up,
r.sum_rate_down from comments com
left join (select comment_id, user_id, sum(rate_up) as sum_rate_up,
sum(rate_down) as sum_rate_down from ratings
group_by comment_id, user_id) as r
on com.comment_id = r.comment_id where page_id = '".$category_id_var."'

Duplicate column name 'user_id' problem

I'm having a problem trying to count the number of user records according to the user's id, however I'm using a subquery to join 2 tables that one has a count parameter but I get an error saying duplicate column name 'user_id.
The query:
$sql = "SELECT loc.location_id,
COUNT(loc.location_id) AS total_records
FROM locations loc
LEFT JOIN
(
SELECT usr.*,
loc.*
FROM
(
members usr
INNER JOIN locations loc
)
WHERE usr.user_id = " . $user_id . "
AND usr.account_disabled = 0
ORDER BY loc.submit_date DESC
) usr ON (loc.user_id = usr.user_id)";
All I need it is to return the user's info and the total_records count done by the COUNT function.
Cheers.
EDIT:
This is what I get returned for this SQL:
SELECT loc.location_id,
loc.street_name,
loc.city,
loc.state,
loc.county,
loc.country,
usr.user_id,
usr.username,
COUNT(loc.location_id) AS total_records
FROM locations loc
INNER JOIN members usr ON (loc.user_id = usr.user_id)
WHERE loc.user_id = $user_id
AND usr.account_disabled = 0
GROUP BY loc.location_id
It's not exactly clear why you've got the derived resultset or the LEFT JOIN. Try this:
SELECT loc.location_id,
COUNT(loc.location_id) AS total_records
FROM LOCATIONS_TABLE loc
INNER JOIN MEMBERS_TABLE usr
ON (loc.user_id = usr.user_id)
WHERE loc.user_id = $user_id
AND usr.account_disabled = 0
GROUP BY loc.location_id
I think the problem is this part:
SELECT usr.*,
loc.*
Both tables have a user_id