Both of these work, but is there a better way to write this?
1.
SELECT asset_id,
asset.category_id,
x,
y
FROM asset
INNER JOIN map_category
ON map_category.category_id = asset.category_id
WHERE asset.map_id = 5
AND map_category.map_id = 5
2. (Added DISTINCT and removed last line)
SELECT DISTINCT asset_id,
asset.category_id,
x,
y
FROM asset
INNER JOIN map_category
ON map_category.category_id = asset.category_id
WHERE asset.map_id = 5
Without either DISTINCT or the last line AND map_cate..., I get 3 records. One for each:
map_category table
asset table
These two queries do completely different things. DISTINCT selects only unique asset_id rows and another query selects only rows where asset.map_id = 5.
The reason you have the same result is your data. On some other data you will have completely different results. So you can't compare efficiency.
since your foreign key consists of both the columns, you should join on both columns...
SELECT asset_id,
asset.category_id,
x,
y
FROM asset
INNER JOIN map_category
ON map_category.category_id = asset.category_id
AND asset.map_id = map_category.map_id
Related
My tables are organized like this:
t_user {name, id}
t_keys {id, key}
t_thingsA {key, thing, ...}
t_thingsB {key, thing, ...}
...
What I'd like to do is for a specific user name, determine the sum of the number of matched rows in t_thingsA, t_thingsB, etc for any key associated with their id.
What I've got is this:
SELECT COUNT(*) FROM t_user INNER JOIN t_keys ON t_user.id=t_keys.id
Just the number of keys. I'm unsure where to go from here - I know I probably have to append some combination of JOINs...
If I understand correctly your question, I think you cannot do it in one query without subqueries or without join tables and doing useless work due to the computation of all the combinations of the rows of t_thingsA with all the rows of t_thingsB (for the same user).
If you only need to compute the number of rows for some specific user_name, I suggest you to do it with a UNION ALL, that is the (computationally) fastest way to do it, but surely not the more elegant one:
SELECT COUNT(*) AS count
FROM t_thingsA
JOIN t_keys ON t_thingsA.key = t_keys.key
JOIN t_user ON t_keys.id = t_user.id
WHERE t_user.user_name = {user_name}
UNION ALL
SELECT COUNT(*) AS count
FROM t_thingsB
JOIN t_keys ON t_thingsB.key = t_keys.key
JOIN t_user ON t_keys.id = t_user.id
WHERE t_user.user_name = {user_name}
The result is that you have in the first row the number of entries in the table thingsA, in second row the number of entries in the table thingsB.
You can also do it in a more compact way, but it isn't efficient.. so I mention it only for completeness:
SELECT
COUNT(DISTINCT(t_thingsA.key)) AS count_thingsA,
COUNT(DISTINCT(t_thingsB.key)) AS count_thingsB
FROM t_user
JOIN t_keys ON t_user.id = t_keys.id
LEFT JOIN t_thingsA ON t_keys.key = t_thingsB.key
LEFT JOIN t_thingsB ON t_keys.key = t_thingsA.key
WHERE t_user.user_name = {user_name}
I'm fetching data from 2 tables according to card number from new_cards table and old_cards table
"I want to display card_code and card_status from theses 2 tables if the card_code contains '8'"
(SELECT *
FROM bills INNER JOIN cats INNER JOIN suppliers
INNER JOIN new_cards
WHERE new_cards.`Card_Code` LIKE '%8%'
AND bills.`Sup_ID` = suppliers.`Sup_ID`
AND new_cards.`Sup_ID` = suppliers.`Sup_ID`
AND cats.`Cat_ID` = bills.`Cat_ID`
AND new_cards.`Cat_ID` = cats.`Cat_ID`
AND new_cards.`Bill_ID` = bills.`Bill_ID`)
UNION
(SELECT * FROM bills INNER JOIN cats INNER JOIN suppliers
INNER JOIN sold_cards WHERE
sold_cards.`Card_Code` LIKE '%8%'
AND bills.`Sup_ID` = suppliers.`Sup_ID`
AND sold_cards.`Sup_ID` = suppliers.`Sup_ID`
AND cats.`Cat_ID` = bills.`Cat_ID`
AND sold_cards.`Cat_ID` = cats.`Cat_ID`
AND sold_cards.`Bill_ID` = bills.`Bill_ID`)
but the result is card_coldes from the first table only which is new_cards
Are you sure there is a match for that? Remove the second query and see if any results are returned. If a combined row doesn't match all criteria from all tables it will not appear. That means there will have to be rows where it joins both new_cards and solid cards. You might be looking for a LEFT JOIN instead of an INNER JOIN
Since you don't mention solid_cards in the new_cards condition or vice versa, it's also going to attempt to join every single row matching the second condition, multiplied by every single row in new_cards. Be careful with OR queries like that, they can be extremely inefficient, especially on large databases.
Tip: To avoid any duplication in query result all records for each row shouldn't have different value.
and if it happens then you can use distinct function.
At the moment, you have a vast swathe of repeated code in your query, which I repeat here laid out more systematically and tagged to indicate repeats:
(SELECT *
FROM bills INNER JOIN cats INNER JOIN suppliers -- ER1A
INNER JOIN new_cards
WHERE new_cards.`Card_Code` LIKE '%8%' -- NR1A
AND bills.`Sup_ID` = suppliers.`Sup_ID` -- ER2A
AND new_cards.`Sup_ID` = suppliers.`Sup_ID` -- NR2A
AND cats.`Cat_ID` = bills.`Cat_ID` -- ER3A
AND new_cards.`Cat_ID` = cats.`Cat_ID` -- NR3A
AND new_cards.`Bill_ID` = bills.`Bill_ID`) -- NR4A
UNION
(SELECT *
FROM bills INNER JOIN cats INNER JOIN suppliers -- ER1B
INNER JOIN sold_cards
WHERE sold_cards.`Card_Code` LIKE '%8%' -- NR1B
AND bills.`Sup_ID` = suppliers.`Sup_ID` -- ER2B
AND sold_cards.`Sup_ID` = suppliers.`Sup_ID` -- NR2B
AND cats.`Cat_ID` = bills.`Cat_ID` -- ER3B
AND sold_cards.`Cat_ID` = cats.`Cat_ID` -- NR3B
AND sold_cards.`Bill_ID` = bills.`Bill_ID`) -- NR4B
The ERxx tags mean 'exact repeat' and NRxx tags mean 'near repeat'. I would structure the UNION over the minimum amount of data, and using ON conditions with the joins, like this:
SELECT *
FROM bills
INNER JOIN cats ON bills.`Cat_ID` = cats.`Cat_ID`
INNER JOIN suppliers ON bills.`Sup_ID` = suppliers.`Sup_ID`
INNER JOIN
(SELECT * FROM new_cards
UNION
SELECT * FROM sold_cards
) AS cards ON cards.`Bill_ID` = bills.`Bill_ID`
WHERE cards.`Card_Code` LIKE '%8%'
AND cards.`Sup_ID` = suppliers.`Sup_ID`
AND cards.`Cat_ID` = cats.`Cat_ID`
The last two conditions in the WHERE clause are also joining conditions; it would be possible to move them up into the ON clauses. However, they should also be redundant — or, if they are not redundant, they are quite likely to be the cause of the trouble you're running into.
An advantage of this reformulation is that you can demonstrate what the UNION sub-query returns by simply executing it. You might prefer to put the filter on Card_Code into the UNION; that way, you see (presumably) fewer rows of data. That could be done permanently; you might end up with no WHERE clause in your outermost query.
You should also test your original query by simply running the second half of the UNION on its own, to see what it returns. If, as you suggest, it returns nothing, then you could debug the second half of the query separately. For exampe, one question for you: do new cards have valid bill codes?
It's not possible to do any more without (minimal) sample data for the tables — say 3 or 4 rows for each of new_cards and sold_cards (with one of the rows in each not matching the filter condition on Card_Code), and then the necessary supporting rows in the bills, cats, and suppliers tables (shouldn't be more than 8 rows in any of those).
You should only show the minimum necessary columns in each table, plus at most one extra column. For example, bills has columns Sup_ID and Bill_ID; you should show those values and perhaps one other ('Bill_Name' or whatever), though the one other isn't really necessary. It also helps to show the minimal schema identifying primary keys and foreign keys. You seem to have a very interlinked set of tables.
You should show the output you get from your query and the output you expect, and explain why the two are different in the light of your sample data.
So after helpful feedback from my original question, I now have this query:
SELECT sessions.id, sessions.title, sessions.abstract, sessions.presenters, sessions.proposal_id, proposals.outcomes, proposals.CategorySelection, proposals.research3, proposals.research4, proposals.research5, proposals.research6, proposals.innovation3, proposals.innovation4, proposals.innovation5,proposals.innovation6, proposals.application3, proposals.application4, proposals.application5, proposals.application6, proposals.integration3, proposals.integration4, proposals.integration5, proposals.integration6, proposals.references, proposals.organization
FROM sessions, proposals
INNER JOIN proposals ON proposals.id = sessions.proposal_id
WHERE sessions.id = '$id
LIMIT 1;)
that is getting me nowhere fast. What am I doing wrong?
Original question:
I need to pull several fields from one table and several more from a second table. The criteria is that a field called proposal_id match the id field of the second table. I am fairly new so this is what I have so far. It is not working, but not sure how to make it work.
(SELECT `title`,`abstract`,`presenters`,`proposal_id` FROM `sessions` WHERE `id`='$id')
UNION
(SELECT `outcomes`,`CategorySelection`,`research3`,`research4`,`research5`,`research6`,`innovation3`,`innovation4`,`innovation5`,
`innovation6`,`application3`,`application4`,`application5`,`application6`,`integration3`,`integration4`,`integration5`,`integration6`,`references`,`organization` FROM `proposals` WHERE `id`= `sessions`.`proposal_id`)
LIMIT 1;
You need to use JOIN not UNION
select
s.*,p.*
from `sessions` s
inner join `proposals` p on p.id = s.proposal_id
where s.id = '$id'
This is how you can join both the tables using the common key between.
You can select the specific fields instead of .* by specifying the column names as
s.col1,s.col2,p.col1,p.col2
etc
Try to use JOINS, where you can match the related fields from both the tables , this is the most convenient way to fetch records from multiple tables
UNION is used when you want to combine two queries
select a.id,b.some_field from table1 as a
INNER JOIN table2 as b ON b.prospal_id = a.id
Take the following:
SELECT
Count(a.record_id) AS newrecruits
,a.studyrecord_id
FROM
visits AS a
INNER JOIN
(
SELECT
record_id
, MAX(modtime) AS latest
FROM
visits
GROUP BY
record_id
) AS b
ON (a.record_id = b.record_id) AND (a.modtime = b.latest)
WHERE (((a.visit_type_id)=1))
GROUP BY a.studyrecord_id;
I want to amend the COUNT part to display a zero if there are no records since I assume COUNT will evaluate to Null.
I have tried the following but still get no results:
IIF(ISNULL(COUNT(a.record_id)),0,COUNT(a.record_id)) AS newrecruits
Is this an issue because the join is on record_id? I tried changing the INNER to LEFT but also received no results.
Q
How do I get the above to evaluate to zero if there are no records matching the criteria?
Edit:
To give a little detail to the reasoning.
The studies table contains a field called 'original_recruits' based on activity before use of the database.
The visits tables tracks new_recruits (Count of records for each study).
I combine these in another query (original_recruits + new_recruits)- If there have been no new recruits I still need to display the original_recruits so if there are no records I need it to evalulate to zero instead of null so the final sum still works.
It seems like you want to count records by StudyRecords.
If you need a count of zero when you have no records, you need to join to a table named StudyRecords.
Did you have one? Else this is a nonsense to ask for rows when you don't have rows!
Let's suppose the StudyRecords exists, then the query should look like something like this :
SELECT
Count(a.record_id) AS newrecruits -- a.record_id will be null if there is zero count for a studyrecord, else will contain the id
sr.Id
FROM
visits AS a
INNER JOIN
(
SELECT
record_id
, MAX(modtime) AS latest
FROM
visits
GROUP BY
record_id
) AS b
ON (a.record_id = b.record_id) AND (a.modtime = b.latest)
LEFT OUTER JOIN studyrecord sr
ON sr.Id = a.studyrecord_id
WHERE a.visit_type_id = 1
GROUP BY sr.Id
I solved the problem by amending the final query where I display the result of combining the original and new recruits to include the IIF there.
SELECT
a.*
, IIF(IsNull([totalrecruits]),consents,totalrecruits)/a.target AS prog
, IIf(IsNull([totalrecruits]),consents,totalrecruits) AS trecruits
FROM
q_latest_studies AS a
LEFT JOIN q_totalrecruitment AS b
ON a.studyrecord_id=b.studyrecord_id
;
I have a select query which selects all products from my inventory table and joins them with two other tables (tables l_products and a_products)
SELECT
i.*,
b.title,
ROUND((i.price/100*80) - l.price,2) AS margin,
l.price AS l_price,
a.price AS a_price,
ROUND((a.price/100*80) - l.price, 2) AS l_margin
FROM inventory i
LEFT JOIN products b ON i.id = b.id
LEFT JOIN a_products a ON i.id = a.id
LEFT JOIN l_products l ON i.id = l.id
WHERE
a.condition LIKE IF(i.condition = 'New', 'New%', 'Used%')
AND l.condition LIKE IF(i.condition = 'New', 'New%', 'Used%')
This select query will normally give me a table such as...
id, title, condition, margin, l_price, a_price ...
001-new ... new 10 20 10
001-used ... used 10 25 20
002....
Now I need a condition in the query which will ignore all used products that are more expensive (have a higher a_price) than their 'new' counterparts, such as in the example above you can see that 001-used has a higher a_price than 001-new.
How can I achieve this with out having to resolve to using php
FULL JOIN this query with it self on a column which has a uniquely same value for each id prefix.
You may achieve this effect by adding another field to your SELECT call which produces same unique value for 001-new and 001-used, 002-new and 002-used...
Such value generation can be done by defining your own SQL Routine to extract first 3 characters from a column.