In my mySQL database I have two tables called job_details and job_actions. In the job_details table, if I get the Detail_ID for Job_ID 41230, then I get five results. For example:
select Detail_ID from job_details where Job_ID = '41230';
What I want to do is use the same Job_ID to get the Percent_Complete for each Detail_ID from the job_actions table. For example, this yields only 4 records because not all of the Detail_IDs appear in this table:
select Detail_ID, Percent_Complete from job_actions where Job_ID = '41230';
I get the same four records when I try to join both tables:
select
details.Detail_ID,
actions.Percent_Complete
from
job_details details,
job_actions actions
where
details.Job_ID = '41230' and
details.Job_ID = actions.Job_ID and
details.Detail_ID = actions.Detail_ID;
I would like my output to include EVERY Detail_ID found in the job_details table, even if it is not found in the job_actions table. For example:
I know how to find the Detail_ID that is missing from the job_actions table, but not how to include it in the results. For example:
select details.Detail_ID from job_details details
left join job_actions actions using (Detail_ID)
where actions.Detail_ID IS NULL and details.Job_ID = '41230';
How can I include the Detail_ID 87679 in the results even though it is missing from the job_actions table?
Never use commas in the FROM clause. Always use proper, explicit, standard JOIN syntax.
You simply want a LEFT JOIN:
select details.Detail_ID, actions.Percent_Complete
from job_details details left join
job_actions actions
on details.Job_ID = actions.Job_ID and
details.Detail_ID = actions.Detail_ID
where details.Job_ID = 41230; -- I assume Job_ID is a number so the single quotes are not necessary
Since, you have wrote the syntax of joins but with older style with where clause that could turn into actually inner join or equi join.
So, use proper explicit join syntax with type of left join
select jd.Detail_ID, ja.Percent_Complete
from job_details jd left join job_actions ja on
ja.Job_ID = jd.Job_ID and
ja.Detail_ID = jd.Detail_ID
where jd.Job_ID = '41230';
You could also use subquery instead since you are looking for all Detail_ID from job_details table
select Detail_ID,
(select Percent_Complete from job_actions
where Job_ID = jd.Job_ID and
Detail_ID = jd.Detail_ID) as Percent_Complete -- Use limit with order by clause in case one or more Percent found.
from job_details jd
where Job_ID = '41230';
I suspect, if you have Job_ID as type of numeric then you don't need to use of quotes just use value (i.e. 41230)
Related
I have 2 tables: contracts_main_list and contracts_detail.
In contracts_main_list I have columns:
user_id
contract_id
and in contracts_detail:
contract_id
other columns with data
I need to select all the rows from the table contracts_main_list WHERE user_id = some number.
From these rows I need to get the list of contract numbers (from column contract_id) and according to them select rows corresponding to each of the contract number from the list. So something like:
WHERE contracts_detail.contract_id = contracts_main_list.contract_id
The contract_ids are probably gonna be unique, but in case there is some kind of error and there will be more rows with the same contract_id in either of the tables, I need to select only one row (so probably using DISTINCT) and select the latest record (both tables have a column id as a primary key)
Also if there is no row in contracts_detail matching with the contract_id to the contract_id of the first table contracts_main_list it should skip the row. But I guess the condition:
WHERE contracts_detail.contract_id = contracts_main_list.contract_id
already covers it.
I hope I made it clear enough. What I am trying to do in real life is show list of contracts with all the relevant data belonging to the user.
To sum this up, I need to find all the contracts belonging to the user and select the rows with details about each contract and finally get the data from the contracts_detail table as a result.
Here is the result you're looking for:
SELECT CD.*
FROM (SELECT C2.contract_id
,MAX(C2.id) AS last_main_list_id
,MAX(CD2.id) AS last_contracts_detail_id
FROM contracts_main_list C2
INNER JOIN contracts_detail CD2 ON CD2.contract_id = C2.contract_id
GROUP BY C2.contract_id) L
INNER JOIN contracts_main_list C ON C.id = L.last_main_list_id
AND C.user_id = ?
INNER JOIN contracts_detail CD ON CD.id= L.last_contracts_detail_id
This query use a subquery for the FROM because of the following indication you provided:
The contract_ids are probably gonna be unique, but in case there is
some kind of error and there will be more rows with the same
contract_id in either of the tables, I need to select only one row
If you're sure that the contract_id are unique, here is the same query without this check on contract_id:
SELECT CD.*
FROM contracts_main_list C
INNER JOIN contracts_detail CD ON CD.contract_id = C.contract_id
WHERE C.user_id = ?
Hope this will help you.
This should be a very simple solution, which means you'll probably think this is a dumb question, but I've tried everything I can think of.
I have two tables, one for possible poll choices, and the other for actual responses. They're structured like so:
choices responses
----------- ----------
poll_id poll_id
choice_id user_id
choice_text choice_id
I have a poll with two choices (yes/no), so I'm trying to fetch results so that if no one has voted for a certain choice, that choice shows up in the result set with a null value. So if 3 users have voted "yes" and none have voted "no", I want the result set to be:
choice_text num
-----------------------------
yes 3
no null
I would have thought this simply an outer join like so:
select
c.choice_text,
count(*) num
from
choices c
left outer join responses r
on c.poll_id = r.poll_id
and c.choice_id = r.choice_id
where
r.poll_id = 1
group by r.choice_id
order by r.choice_id asc;
But alas, that is giving me:
choice_text num
-----------------------------
yes 3
...without any record for "no".
I've tried every language for joins I can think of, with the same erroneous result.
Thoughts?
Your where condition on the outer joined table turns the outer join into an inner join. Move that condition into the JOIN
select c.choice_text,
count(*) num
from choices c
left outer join responses r
on c.poll_id = r.poll_id
and c.choice_id = r.choice_id
and r.poll_id = 1
group by r.choice_id
order by r.choice_id asc;
Btw: your usage of group by is incorrect and every other DBMS would reject that statement. MySQL simply chooses to return random data instead of failing with an error. For more details please see these blogs:
Debunking GROUP BY myths
Wrong GROUP BY makes your queries fragile
Figured it out thanks to #a_horse_with_no_name pointing me in the right direction.
The winning query:
select c.choice_text,
count(r.user_id) num
from choices c
left outer join responses r
on c.poll_id = r.poll_id
and c.choice_id = r.choice_id
and c.poll_id = 1
group by c.choice_id
order by c.choice_id asc;
Moved the c.poll_id clause to the join, like #a_horse_with_no_name suggested, but then had to group by the choices table instead of the responses table, and finally changed my count(*) to count(r.user_id), and voila.
Given the following query…
SELECT DISTINCT *
FROM PAS_Post
WHERE post_user_id = 21
GROUP BY post_post_id
UNION
SELECT DISTINCT PAS_Post.*
FROM PAS_Follow LEFT JOIN PAS_Post ON (
PAS_Follow.folw_followed_user_id = PAS_Post.post_user_id
)
WHERE PAS_Follow.folw_follower_user_id = 21
GROUP BY post_post_id
ORDER BY post_posted_date DESC
I always get a row in the results that is just NULL's, unfortunately I need to preserve some NULL values in the data as the Post's table (PAS_Post) holds different types of information.
Can anyone steer me in the right direction to get rid of this null row.
I do not want or need the last row here
You're using a (left) outer join in the second part of the UNION, so any cases that do not satisfy the join criteria will result in data from the table on the left of the join (PAS_Follow), but NULL in every column of the table on the right of the join (PAS_Post); the subsequent selection only of columns from the latter table results in the NULL rows that you observe. Therefore, the simplest solution is to use an inner join (that completely excludes records where the join criteria is not met).
However, in your case, it appears that your query can be greatly simplified by simply using two possible conditions in a filter on the joined tables rather than a UNION:
SELECT p.*
FROM PAS_Post AS p
JOIN PAS_Follow AS f ON f.folw_followed_user_id = p.post_user_id
WHERE p.post_user_id = 21
OR f.folw_follower_user_id = 21
ORDER BY p.post_posted_date DESC
I have excluded the GROUP BY clause on the assumption that post_post_id is the primary key (or at very least is UNIQUE) in your PAS_Post table. If that assumption is incorrect, you may want to reintroduce it—but beware that MySQL will indeterminately select the values that will be returned from each group.
I have 2 tables, the first one is MASTER_TABLE with fields ID, STATUS_CODE, STATUS_SUBCODE, SUBJECT_CODE, SUBJECT_SUBCODE and the second table is CODE_TABLE which has the unique description for every combination of a code and subcode. It has the following fields CODE, SUBCODE and DESCRIPTION
How to write a query to retrieve the ID, STATUS and SUBJECT , for example for every combination of STATUS_CODE and STATUS_SUBCODE in MASTER_TABLE I have to get the STATUS value in CODE_TABLE, similarly I have to do the same thing for SUBJECT
You must join twice to CODE_TABLE - once for each type of look-up, so to distinguish the rows from eachother, you must alias at least one (but usually one would alias both, as below):
select
mt.ID,
ct1.DESCRIPTION as STATUS
ct2.DESCRIPTION as SUBJECT
from MASTER_TABLE mt
left join CODE_TABLE ct1
on ct1.CODE = mt.STATUS_CODE and ct1.SUBCODE = mt.STATUS_SUBCODE
left join CODE_TABLE ct2
on ct2.CODE = mt.SUBJECT_CODE and ct2.SUBCODE = mt.SUBJECT_SUBCODE
I have made the joins left joins in case data is missing from CODE_TABLE, in which case this query would produce a null for the corresponding description.
I am trying to create a custom sort that involves the count of some records in another table. For example, if one record has no records associated with it in the other table, it should appear higher in the sort than if it had one or more records. Here's what I have so far:
SELECT People.*, Organizations.Name AS Organization_Name,
(CASE
WHEN Sent IS NULL AND COUNT(SELECT * FROM Graphics WHERE People.Organization_ID = Graphics.Organization_ID) = 0 THEN 0
ELSE 1
END) AS Status
FROM People
LEFT JOIN Organizations ON Organizations.ID = People.Organization_ID
ORDER BY Status ASC
The subquery within the COUNT is not working. What is the correct way to do something like this?
Update: I moved the case statement into the order by clause and added a join:
SELECT People.*, Organizations.Name AS Organization_Name
FROM People
LEFT JOIN Organizations ON Organizations.ID = People.Organization_ID
LEFT JOIN Graphics ON Graphics.Organization_ID = People.Organization_ID
GROUP BY People.ID
ORDER BY
CASE
WHEN Sent IS NULL AND Graphics.ID IS NULL THEN 0
ELSE 1
END ASC
So if if the People record does not have any graphics, Graphics.ID will be null. This achieves the immediate need.
If what you tried does not work, it can be done by joining against a subquery, and placing the CASE expression into ORDER BY as well:
SELECT
People.*,
orgcount.num
FROM People JOIN (
SELECT Organization_ID, COUNT(*) AS num FROM Graphics GROUP BY Organization_ID
) orgcount ON People.Organization_ID = orgcount.num
ORDER BY
CASE WHEN Sent IS NULL AND orgcount.num = 0 THEN 0 ELSE 1 END,
orgcount.num DESC
You could use an outer join to the Graphics table to get the data needed for your sort.
Since I don't know your schema, I made an assumption that the People table has a primary key column called ID. If the PK column has a different name, you should substitute that in the GROUP BY clause.
Something like this should work for you:
SELECT People.*, (count(Distinct Graphics.Organization_ID) > 0) as Status
FROM People
LEFT OUTER JOIN Graphics ON People.Organization_ID = Graphics.Organization_ID
GROUP BY People.ID
ORDER BY Status ASC
Fairly straight forward with a LEFT JOIN provided you have some kind of primary key in the People table to GROUP on;
SELECT p.*, sent IS NOT NULL or COUNT(g.Organization_ID) Status
FROM People p LEFT JOIN Graphics g ON g.Organization_ID = p.Organization_ID
GROUP BY p.primary_key
ORDER BY Status
Demo here.