MySQL query for multiple table data - mysql

I'm searching for a solution to a problem within MySQL which doesn't sound too complicated, I thought.
Basically I want to use two tables.
The first does contain an electronic component list like
ID Description Value AdditonalInfo
1 Resistor 1.0R R0402
2 Capacitor 100nF C0805
3 Capacitor 10nF C0603
...
I want to store information about the sourcing within a second table.
ID Component Manufacturer Partnumber Timestamp
1 2 TDK XXXYYYZZZ 5
2 2 Kemet AAABBBCCC 10
3 1 Multicomp 111222333 3
...
As you can see, it should be possible to add more than one manufacturer for each component.
Now, I want to generate a single table (a view) which should contain
all component information AND if present, the latest entry of the manufacturer.
For the given example that would be
ID Description Value AdditonalInfo Manufacturer Partnumber
1 Resistor 1.0R R0402 Multicomp 111222333
2 Capacitor 100nF C0805 Kemet AAABBBCCC
3 Capacitor 10nF C0603 (NULL)
Would this be possible within a single query? Or at least with some kind of query which
generates the final table? I could not find out, if the JOIN command would do that.
I would appreciate any help or hints to find a solution for this.
Thanks!

The following query should give you what you are after.
It takes all the components, and then for each component shows the matching entries in the sub-query against the sourcing table which groups the components by the latest entry.
The sub-query is joined based on the component and max(timestamp) to another copy of the sourcing table to get the remaining information required.
SELECT a.ID, a.Description, a.Value, a.AdditonalInfo,
c.Manufacturer, c.Partnumber
FROM componentTable a
LEFT JOIN ( SELECT component, max(timestamp) AS maxTime
FROM sourcingTable
GROUP BY component
) b
ON a.id = b.component
INNER JOIN sourcingTable c
ON b.component = c.component
AND b.maxTime = c.timestamp
You may need additional bracketing around the LEFT JOIN and INNER JOIN parts, but give this a try first and let me know if it doesn't work

Have your try this:
SELECT * FROM table1 LEFT JOIN table2 on table1.id = table2.id

You can get data using query with JOIN, if you have index on Component column:
SELECT
*
FROM components
INNER JOIN store USING(id)
This query will enough and you don't need store redundant data.

Related

SQL Select - Some Rows Won't Display

I have two tables. One of them named files and there is al list of all files. the second table called payments, and there is in there a list of payments for some files.
Payments:
id | fileid | {...}
1 2
2 3
3 2
Files:
id | {...}
1
2
3
I want to select all files, and join the table payments to order by count of this table.
In this case, the first row will be file #2, because it repeats the most in the payments table.
I tried to do it, but when I do it - not all of the rows are shown!
I think it happens because not all of the files are in the payments table. So in this case, I think that it won't display the first row.
Thanks, and sorry for my English
P.S: I use mysql engine
** UPDATE **
My Code:
SELECT `id`,`name`,`size`,`downloads`,`upload_date`,`server_ip`,COUNT(`uploadid`) AS numProfits
FROM `uploads`
JOIN `profits`
ON `uploads`.`id` = `profits`.`uploadid`
WHERE `uploads`.`userid` = 1
AND `removed` = 0
ORDER BY numProfits
As others have noted you need to use LEFT JOIN. - This tells MySQL that entries from the tables to the left should be included even if no corresponding entries exists in the table on the right.
Also you should use GROUP BY to indicate how the COUNT should be deliminated.
So the SQL should be something like;
SELECT Files.ID, count(Payments.FileID) as numpays FROM
Files
LEFT OUTER JOIN
Payments
ON Files.id=Payments.FileID
GROUP BY files.ID
ORDER BY numpays desc
SQL Fiddle
Try this:
select B.fileid,A.{}.....
from
(select id,.....
from files A
inner join
(select count(*),fileid,.....
from payments
group by fileid) B
on files.id=payments.fileid)
I hope this helps. I'm assuming that all ID in files table are unique. In this answer, you can apply an order by clause as per your wish. I've left the select statement to you to select whatever data you want to fetch.
As far as your problem is described, I think this should work. If any problems, do post a comment.
Try LEFT JOIN - in MySQL, the default JOIN is actually an INNER JOIN. In an INNER JOIN, you will only get results back that are in both sides of the join.
See: Difference in MySQL JOIN vs LEFT JOIN
And, as noted in the comments, you may need a GROUP BY with your COUNT as well, to prevent it from just counting all the rows that come back.

MySQL joins count matched record from another table

What I have is 2 tables, the first table I want it to display all results, no "where" or anything to limit it.
The second table I want to match an id to the first table, it can have multiple rows referencing it so I want to count the number.
So lets say the first table is like this:
ID - name
1 - one
2 - two
3 - three
4 - four
And the second table is like this
ID - REF
1 - 1
2 - 1
3 - 2
4 - 2
5 - 3
6 - 3
7 - 4
8 - 4
I want to combine them like so:
ID - name - count
1 - one - 2
2 - two - 2
3 - three- 2
4 - four - 2
I have tried using subqueries, left joins, right joins, inner joins, sub query joins, grouping and 9 times out of ten I get 20 results of the first ID out of 1300 results I should get. The rest I only get an incorrect count and no name.
I feel this is MySQL 101 but after attempting multiple variations and coming up with nothing I feel there must be something I am missing.
I would be happy to be directed to a question that is in the exact same situation (2 hours of looking and nothing that works exactly like this) Or a simple query to point out the logic of this method, Thanks in an advance to anyone that answers, you will have made my day.
If any additional information is needed let me know, I have left out the query deliberately because I have adapted it so many times that it will not have much relevance (I would have to list every query I tried and that would be far to much scrolling)
Ok I have tested the first and answer and it seemed to work in this context so I will expand my answer, the question is "answered" so this is just an expansion if there are no replies I will close this with the answer as follows:
SELECT t.id, t.name, count(*) AS suppliers
FROM #__tiresku AS t
LEFT JOIN #__mrsp AS m ON t.name = m.tiresku_id
GROUP BY t.id, t.name
The expansions is an inner join, I have another table that is more of a list, it has an id and a name and that's it, I reference that table with an id to get the "name" instead.
This might have a better option then joins (like foreign keys or something).
I had this added to the select b.name AS brand_name
And a join INNER JOIN #__brands AS b ON t.brand = b.id
Worked with a sub query rather then join
This is a basic join with aggregation:
select t1.id, t1.name, count(*) as `count`
from table1 t1 join
table2 t2
on t1.id = t2.ref
group by t1.id, t1.name;
As asked, the example does not include records in the first table that are not in the second table, but this may be possible and is implied.
I am inclined to create a nested table of the counts in the second table without regard to "exists in the first table" either, unless the counts are huge and then the probe becomes cheaper.
I would do the count of the values in the second table first as the first table is a defacto decode of a description.
select ID, name, coalesce('count',0)
from (select ref, count(*) as 'count'
from table2
group by ref) as T2
right join table1
on ref = ID;

SELECT where ID matches one of a list

I'm just learning the ins and outs of MYSQL queries but I've run into a roadblock with a project and I'd appreciate some help reaching a solution.
I have two tables, the first contains a reference to the entry (entry_id) and the modification associated (mod_id) as well as the category of modification it refers to (mod_name)
Table 1: exp_store_product_options
mod_id entry_id mod_name
3 2919 COLOR
4 2311 SIZE
5 2311 COLOR
6 3301 COLOR
the second table contains two relevant fields, mod_id and opt_name
Table 2: exp_store_product_modifiers
mod_id opt_name
3 BLACK
4 EU 44
5 BROWN
6 BROWN
What I am trying to achieve, is a listing of all the DISTINCT opt_name fields that (through a join on the mod_id) correspond to entry_ids that I would like to pass in as a lump.
here is the code I've come up with so far, I believe it'll do what I need aside from requiring me to loop through the query for each entry id, and failing on the DISTINCT requirement since for each iteration, everything is distinct. (the {sale_products} tags are from ExpressionEngine, and will loop during the parse to provide me with a list of the entry_id results that are relevant to this page
SELECT DISTINCT opt_name
FROM exp_store_product_options
INNER JOIN exp_store_product_modifiers
ON exp_store_product_options.product_mod_id=exp_store_product_modifiers.product_mod_id
{sale_products parse='inward'}entry_id = '{entry_id}' OR{/sale_products}
AND mod_name = 'SIZE'
====================================================
POSTMORTEM
Just in case anyone else is trying to work within expresso's Store module for ExpressionEngine and needs to build some rudimentary filtering into your templates, here's the code I ultimately got to work. Very similar to Ben's code, but utilizing embeds instead of directly inserting the entry_ids because of issues with parse order:
the template: embeds/product_filter
{exp:query sql="SELECT DISTINCT opt_name
FROM exp_store_product_modifiers
LEFT JOIN exp_store_product_options
ON exp_store_product_options.product_mod_id = exp_store_product_modifiers.product_mod_id
WHERE exp_store_product_modifiers.entry_id IN ({embed:entry_ids})
AND exp_store_product_modifiers.mod_name = '{embed:filter_on}'"
}
<li>{opt_name}</li>
{/exp:query}
with an {embed} that looks like
{embed="embeds/product_filter" entry_ids="{sale_products backspace='1'}{entry_id},{/sale_products}" filter_on="SIZE"}
If you have a list of entries, you can use IN. Also, I'd use a LEFT JOIN here instead of an INNER JOIN.
SELECT DISTINCT opt_name
FROM exp_store_product_options
LEFT JOIN exp_store_product_modifiers
ON exp_store_product_options.product_mod_id = exp_store_product_modifiers.product_mod_id
WHERE exp_store_product_options.entry_id IN (1,2,3)
AND mod_name = 'SIZE'

Restricting sub-query to 2 arguments in same row

It's been hours that I've been searching for a solution (books, internet, etc) and can't find anything. Here's my problem:
I got a table of items being tagged by 2 criteria: let's name them crit1 and crit2. For each of the items I can have for crit1 and crit2 the following int example values (criteria1,criteria2): (1,5) (5,2) (4,7) (8,6), etc.
In another table I store users that are subscribed to some of these items filtered by the above criteria. Let's say that user_id 1 is subscribed to the following item type (criteria1, criteria2): (1,5) (2,7). When I make my query to fetch the items that user 1 is subscribed to, I get the items tagged with (1,5) (2,7) but also (1,7) or (2,5). The SQL Select query is making cross-comparisons between each row.
Generally, I would like to know how to make a query that is filtered from more than 1 field in the same row (no cross-row allowed).
I tried to use JOINS to sort the problem but I can't link criteria1 and criteria2 in the same JOIN. I have to use 2 JOINS and that makes them independent (and the cross-comparison between criteria1 and criteria2 will happen).
Use AND operator in ON clause for JOIN:
SELECT i.* FROM subscriptions s
JOIN items i
ON i.crit1 = s.crit1 AND i.crit2 = s.crit2
WHERE s.user_id = 1
without seeing the rest of the structure specifically, I'll give it a shot like...
select
i.itemid,
i.itemDescription,
s.subscriberName
from
items i
join Subscribers s
on i.category1 = s.category1
AND i.category2 = s.category2
If this isn't it, you might need to dump some sample data from each respective table of what you are trying to actually get.
Try this:
multiply crit 1 by 1000 (or some other number-depending or size of numbers) and add to it Crit2
Use that formula in your where or join

Using Joins, Group By and Sub Queries, Oh My!

I have a database with a table for details of ponies, another for details of contacts (owners and breeders), and then several other small tables for parameters (colours, counties, area codes, etc.). To give me a list of existing pony profiles, with their various details given, i use the following query:
SELECT *
FROM profiles
INNER JOIN prm_breedgender
ON profiles.ProfileGenderID = prm_breedgender.BreedGenderID
LEFT JOIN contacts
ON profiles.ProfileOwnerID = contacts.ContactID
INNER JOIN prm_breedcolour
ON profiles.ProfileAdultColourID = prm_breedcolour.BreedColourID
ORDER BY profiles.ProfileYearOfBirth ASC $limit
In the above sample, the 'profiles' table is my primary table (holding the Ponies info), 'contacts' is second in importance holding as it does the owner and breeder info. The lesser parameter tables can be identified by their prm_ prefix. The above query works fine, but i want to do more.
The first big issue is that I wish to GROUP the results by gender: Stallions, Mares, Geldings... I used << GROUP BY prm_breedgender.BreedGender >> or << GROUP BY ProfileBreedGenderID >> before my ORDER BY line, but than only returns two results from all my available profiles. I have read up on this, and apparantly need to reorganise my query to accomodate GROUP within my primary SELECT clause. How to do this however, gets me verrrrrrry confused. Step by step help here would be fantabulous.
As a further note on the above - You may have noticed the $limit var at the end of my query. This is for pagination, a feature I want to keep. I shouldn't think that's an issue however.
My secondary issue is more of an organisational one. You can see where I have pulled my Owner information from the contacts table here:
LEFT JOIN contacts
ON profiles.ProfileOwnerID = contacts.ContactID
I could add another stipulation:
AND profiles.ProfileBreederID = contacts.ContactID
with the intention of being able to list a pony's Owner and Breeder, where info on either is available. I'm not sure how to echo out this info though, as $row['ContactName'] could apply in either the capacity of owner OR breeder.
Is this a case of simply running two queries rather than one? Assigning a variable $foo to the first run of the query, then just run another separate query altogether and assign $bar to those results? Or is there a smarter way of doing it all in the one query (e.g. $row['ContactName']First-iteration, $row['ContactName']Second-iteration)? Advice here would be much appreciated.
And That's it! I've tried to be as clear as possible, and do really appreciate any help or advice at all you can give. Thanks in advance.
##########################################################################EDIT
My query currently stands as an amalgam of that provided by Cularis and Symcbean:
SELECT *
FROM (
profiles
INNER JOIN prm_breedgender
ON profiles.ProfileGenderID = prm_breedgender.BreedGenderID
LEFT JOIN contacts AS owners
ON profiles.ProfileOwnerID = owners.ContactID
INNER JOIN prm_breedcolour
ON profiles.ProfileAdultColourID = prm_breedcolour.BreedColourID
)
LEFT JOIN contacts AS breeders
ON profiles.ProfileBreederID = breeders.ContactID
ORDER BY prm_breedgender.BreedGender ASC, profiles.ProfileYearOfBirth ASC $limit
It works insofar as the results are being arranged as I had hoped: i.e. by age and gender. However, I cannot seem to get the alias' to work in relation to the contacts queries (breeder and owner). No error is displayed, and neither are any Owners or Breeders. Any further clarification on this would be hugely appreciated.
P.s. I dropped the alias given to the final LEFT JOIN by Symcbean's example, as I could not get the resulting ORDER BY statement to work for me - my own fault, I'm certain. Nonetheless, it works now although this may be what is causing the issue with the contacts query.
GROUP in SQL terms means using aggregate functions over a group of entries. I guess what you want is order by gender:
ORDER BY prm_breedgender.BreedGender ASC, profiles.ProfileYearOfBirth ASC $limit
This will output all Stallions, etc. next to each other.
To also get the breeders contact, you need to join with the contacts table again, using an alias:
LEFT JOIN contacts AS owners
ON profiles.ProfileOwnerID = owners.ContactID
LEFT JOIN contacts AS breeders
ON profiles.ProfileBreederID = breeders.ContactID
To further expand on what #cularis stated, group by is for aggregations down to the lowest level of "grouping" criteria. For example, and I'm not doing per your specific tables, but you'll see the impact. Say you want to show a page grouped by Breed. Then, a user picks a breed and they can see all entries of that breed.
PonyID ProfileGenderID Breeder
1 1 1
2 1 1
3 2 2
4 3 3
5 1 2
6 1 3
7 2 3
Assuming your Gender table is a lookup where ex:
BreedGenderID Description
1 Stallion
2 Mare
3 Geldings
SELECT *
FROM profiles
INNER JOIN prm_breedgender
ON profiles.ProfileGenderID = prm_breedgender.BreedGenderID
select
BG.Description,
count(*) as CountPerBreed
from
Profiles P
join prm_BreedGender BG
on p.ProfileGenderID = BG.BreedGenderID
group by
BG.Description
order by
BG.Description
would result in something like (counts are only coincidentally sequential)
Description CountPerBreed
Geldings 1
Mare 2
Stallion 4
change the "order by" clause to "order by CountsPerBreed Desc" (for descending) and you would get
Description CountPerBreed
Stallion 4
Mare 2
Geldings 1
To expand, if you wanted the aggregations to be broken down per breeder... It is a best practice to group by all things that are NOT AGGREGATES (such as MIN(), MAX(), AVG(), COUNT(), SUM(), etc)
select
BG.Description,
BR.BreaderName,
count(*) as CountPerBreed
from
Profiles P
join prm_BreedGender BG
on p.ProfileGenderID = BG.BreedGenderID
join Breeders BR
on p.Breeder = BR.BreaderID
group by
BG.Description,
BR.BreaderName
order by
BG.Description
would result in something like (counts are only coincidentally sequential)
Description BreaderName CountPerBreed
Geldings Bill 1
Mare John 1
Mare Sally 1
Stallion George 2
Stallion Tom 1
Stallion Wayne 1
As you can see, the more granularity you provide to the group by, the aggregation per that level is smaller.
Your join conditions otherwise are obviously understood from what you've provided. Hopefully this sample clearly provides what the querying process will do. Your group by does not have to be the same as the final order... its just common to see so someone looking at the results is not trying to guess how the data was organized.
In your sample, you had an order by the birth year. When doing an aggregation, you will never have the specific birth year of a single pony to so order by... UNLESS.... You included the YEAR( ProfileYearOfBirth ) as BirthYear as a column, and included that WITH your group by... Such as having 100 ponies 1 yr old and 37 at 2 yrs old of a given breed.
It would have been helpful if you'd provided details of the table structure and approximate numbers of rows. Also using '*' for a SELECT is a messy practice - and will cause you problems later (see below).
What version of MySQL is this?
apparantly need to reorganise my query to accomodate GROUP within my primary SELECT clause
Not necessarily since v4 (? IIRC), you could just wrap your query in a consolidating select (but move the limit into the outer select:
SELECT ProfileGenderID, COUNT(*)
FROM (
[your query without the LIMIT]
) ilv
GROUP BY ProfileGenderID
LIMIT $limit;
(note you can't ORDER BY ilv.ProfileYearOfBirth since it is not a selected column / group by expression)
How many records/columns do you have in prm_breedgender? Is it just Stallions, Mares, Geldings...? Do you think this list is likely to change? Do you have ponies with multiple genders? I suspect that this domain would be better represented by an enum in the profiles table.
with the intention of being able to list a pony's Owner and Breeder,
Using the code you suggest, you'll only get returned instances where the owner and breeder are the same! You need to add a second instance of the contacts table with a different alias to get them all, e.g.
SELECT *
FROM (
SELECT *
FROM profiles
INNER JOIN prm_breedgender
ON profiles.ProfileGenderID = prm_breedgender.BreedGenderID
LEFT JOIN contacts ownerContact
ON profiles.ProfileOwnerID = ownerContact.ContactID
INNER JOIN prm_breedcolour
ON profiles.ProfileAdultColourID = prm_breedcolour.BreedColourID
) ilv LEFT JOIN contacts breederContact
ON ilv.ProfileBreederID = breederContact.ContactID
ORDER BY ilv.ProfileYearOfBirth ASC $limit