MySQL summing number of rows found across multiple tables - mysql

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}

Related

where clause containing union returns the first query only

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.

Need mysql query to pull data from two tables

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

COUNT evaluate to zero if no matching records

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
;

Getting Repeated values in SQL

I was desperately trying harder and harder to get this thing done but didn`t yet succeed. I am getting repeated values when i run this query.
select
tbl_ShipmentStatus.ShipmentID
,Tbl_Contract.ContractID,
Tbl_Contract.KeyWinCountNumber,
Tbl_Item.ItemName,
Tbl_CountryFrom.CountryFromName,
Tbl_CountryTo.CountryToName,
Tbl_Brand.BrandName,
Tbl_Count.CountName,
Tbl_Seller.SellerName,
Tbl_Buyer.BuyerName,
Tbl_Contract.ContractNumber,
Tbl_Contract.ContractDate,
tbl_CountDetail.TotalQty,
tbl_CostUnit.CostUnitName,
tbl_Comission.Payment,
tbl_Port.PortName,
Tbl_Contract.Vans,
tbl_Comission.ComissionPay,
tbl_Comission.ComissionRcv,
tbl_CountDetail.UnitPrice,
tbl_Comission.ComissionRemarks,
tbl_CountDetail.Amount,
tbl_LCStatus.LCNumber,
tbl_ShipmentStatus.InvoiceNumber,
tbl_ShipmentStatus.InvoiceDate,
tbl_ShipmentStatus.BLNumber,
tbl_ShipmentStatus.BLDate,
tbl_ShipmentStatus.VesselName,
tbl_ShipmentStatus.DueDate
from tbl_ShipmentStatus
inner join tbl_LCStatus
on
tbl_LCStatus.LCID = tbl_ShipmentStatus.LCStatusID
inner join Tbl_Contract
on
tbl_LCStatus.ContractID = Tbl_Contract.ContractID
inner join Tbl_CountDetail
on Tbl_Contract.ContractID = Tbl_CountDetail.ContractId
inner join tbl_Comission
on
tbl_Comission.ContractID = Tbl_Contract.ContractID
inner join Tbl_Item
on
Tbl_Item.ItemID = Tbl_Contract.ItemID
inner join Tbl_Brand
on Tbl_Brand.BrandID = Tbl_Contract.BrandID
inner join Tbl_Buyer
on Tbl_Buyer.BuyerID = Tbl_Contract.BuyerID
inner join Tbl_Seller
on Tbl_Seller.SellerID = Tbl_Contract.SellerID
inner join Tbl_CountryFrom
on Tbl_CountryFrom.CountryFromID = Tbl_Contract.CountryFromID
inner join Tbl_CountryTo
on
Tbl_CountryTo.CountryToID = Tbl_Contract.CountryToID
inner join Tbl_Count
on
Tbl_Count.CountID = Tbl_CountDetail.CountId
inner join tbl_CostUnit
on tbl_Comission.CostUnitID = tbl_CostUnit.CostUnitID
inner join tbl_Port
on tbl_Port.PortID = tbl_Comission.PortID
where tbl_LCStatus.isDeleted = 0
and tbl_ShipmentStatus.isDeleted =0
and tbl_LCStatus.isDeleted = 0
and Tbl_CountDetail.isDeleted = 0
and Tbl_Contract.isDeleted = 0
and tbl_ShipmentStatus.LCStatusID = 5
I have also attached a picture of my result set of rows.
Any suggestions why this is happening would really be appreciable.
Result Set
Typically this happens when you have an implicit partial cross join (Cartesian product) between two of your tables. That's what it looks like to me here.
This happens most often when you have a many-to-many relationship. For example, if a single Album allows both multiple Artists and multiple Songs and the only relationship between Artists and Songs is Album, then there's essentially a many-to-many relationship between Artists and Songs. If you select from all three tables at once you're going to implicitly cross join Artists and Songs, and this may not be what you want.
Looking at your query, I see many-to-many between Tbl_CountDetail and tbl_Comission through Tbl_Contract. Try eliminating one of those joins to test to see if the behavior disappears.
Try using the DISTINCT keyword. It should solve your issue
Select DISTINCT ....
Wait as far as I can see your records are not duplicates.
HOWEVER
Notice the CountName column and Shipment ID column
The combination is unique for every row. Hence the values are unique as far as I can see. Try not selecting CountName.
Well if you have distinct rows its not a duplication problem. The issue is during the join a combination is occurring you don't want it to duplicating the results.
Either don't select CountName or you have a mistake in your data.
Only one of those rows should be true either 6 with Count2 or 6 with Count1. Likewise for 7. The fact that your getting both when your not supposed to indicates a logic mistake

Most efficient SQL, DISTINCT or WHERE...AND

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