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;
Related
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.
I'm keep getting lost in this one and struggling to find the right method. Hopefully someone out there might know of a good way of doing what I want to do.
I have two tables and I want to update one table using concatenated data from the other where the ids are the same. As an example....
Table1
ItemID CategoryID
1 20
1 30
1 40
2 10
3 40
3 20
4 10
4 20
Table2
ItemID CatIDs
1
2
3
4
I want to update Table2.CatIDs with all the Category IDs from Table1 where the ItemIDs match. It seems straightforward when I write it down like that but after trying Inner Joins, Sub Queries and so on as I've found online, I keep getting "You have errors in your SQL Syntax..."
I want Table2 to look something like
ItemID CatIDs
1 20,30,40
2 10
3 40,20
4 10,20
I've tried Inner Joins and also sub queries and the closest I've got without an error was this....
UPDATE Table2
SET Table2.CatIDs = Table2.CatIDs + ", " +
(SELECT CategoryID FROM Table1 WHERE Table2.ItemID = Table1.ItemID)
But it doesn't seem finished and all it done was update four rows with the same CatIDs and then give me the message
#1242 - Subquery returns more than 1 row
I'm sure someone out there will be able to see where I'm going wrong and point me in the right direction.
Thanks in advance
You should realize that table1 is the right way to store this information. It is called a junction or association table. Sometimes you need to do the concatenation for presentation purposes, but you need to keep the junction table for full flexibility.
You can do what you want using an update with join and group by:
UPDATE Table2 t2 JOIN
(SELECT t1.ItemId, GROUP_CONCAT(t1.CategoryId SEPARATOR ', ') as cats
FROM table1 t1
GROUP BY t1.ItemId
) tc
ON t2.ItemId = tc.ItemId
SET t2.CatIDs = tc.cats;
Try like below by using group_concat() you can get the , separated list and then join between the tables. But storing comma separated values is never a good idea.
update table2 t2
join
(
select ItemID,group_concat(CategoryID) as newcat
from table1 group by ItemID
) tab on t2.ItemID = tab.ItemID
set t2.CatIDs = tab.newcat
I know this has to be a fairly common issue, and I am sure the answer is readily available but I am not sure how to phrase my search so I have been forced to troubleshoot this on my own for the most part.
Table A
id | content_id | score
1 | 2 | 16
2 | 2 | 4
3 | 3 | 8
4 | 3 | 12
Table B
id | content
1 | "Content Goes Here"
2 | "Content Goes Here"
3 | "Content Goes Here"
Objective: SUM all scores from table A, group by the unique content_id and show the content associated with the id, ordered by the sum score.
Current Working Query:
SELECT a.content_id, b.content, SUM(a.score) AS sum
FROM table_a a
LEFT JOIN table_b b ON a.content_id = b.id
GROUP BY a.content_id
ORDER BY sum ASC;
Problem: As far as I can tell, with the way I have structured my query, the content is grabbed from table_b by looping through each record on table_a, checking for a record in table_b with an identical id, and grabbing the content field. The problem here is that in table_a there is nearly 500k+ records, and in table_b there is 112 records. Which means that potentially 500,000 x 112 cross table lookups/matches are being performed just to attached 112 unique content fields to a total of 112 results in the ending result set.
HELP!: How do I more efficiently append the 112 content fields from table_b to the 112 results produced by the query? I am guessing it has something to do with the query execution order, like somehow only looking for and appending the content field to the matched result row AFTER the sums are produced and it is narrowed down to only 112 records? Have studied the MySQL API and benchmarked various subqueries, several joins, and even tried playing with UNION. It is probably something abundandtly obvious to you guys, but my brain just can't get around it.
FYI: Like mentioned earlier, the query does work. The results are produced in about 8 to 10 seconds, and of course each subsequent query after that is immediate because of query caching. But for me, with how simple this is, I know that 8 seconds can at LEAST be cut in half. I just feel it deep down in my guts. Right deep down in my gutssss.
I hope this is concise enough, if I need to clarify or explain something better please let me know! Thanks in advance.
The MySQL query optimiser only allows "nested loop joins" ** These are the internal operators for how an INNER join is evaluated. Other RDBMS allow other kinds of JOINs which are more efficient.
However, in your case you can try this. Hopefully the optimiser will do the aggregate before the JOIN
SELECT
a.content_id, b.content a.sum
FROM
(
SELECT content_id, SUM(score) AS sum
FROM table_a
GROUP BY content_id
) a
JOIN table_b b ON a.content_id = b.id
ORDER BY
sum ASC;
In addition, if you don't want the results ordered you can use ORDER BY NULL which usually removes a filesort from the EXPLAIN. And of course, I assume that there are indexes on the 2 content_id columns (one primary key, one foreign key index)
Finally, I would also assume that an INNER JOIN will be enough: every a.contentid exists in tableb. If not, you are missing a foreign key and index on a.contentid
** It's getting better but you need MariaDB or MySQL 5.6
This should be a little faster:
SELECT
tmp.content_id,
b.content,
tmp.asum
FROM (
SELECT
a.content_id,
SUM(a.score) AS asum
FROM
table_a a
GROUP BY
a.content_id
ORDER BY
NULL
) as tmp
LEFT JOIN table_b b
ON tmp.content_id = b.id
ORDER BY
tmp.asum ASC
You can use EXPLAIN to check the query execution plan for both queries when you want to benchmark them
This is my first time asking a question on here. It has been very helpful with learning.
I am trying to select a table and getting only rows that have a maximum value for its particular group in another table. One of the best answers that is very close but not quite there is this one (SQL Select only rows with Max Value on a Column) but it only relates to a single table. I have found some others with multiple table but not sure how exactly to use it.
I have a table with (simplified)
prodID, quantity, mach, etc
I then have a table with
prodStatusID, prodID, userID, subStatusID
a last table with sub status names
subStatusID, subStatusName
I am trying to get a table with all of the first table and the second table but only with the row that has the maximum status number and include the right status name.
My other concern which may not matter now but in a year or two when this thing starts to really fill up is performance. I dont know bad it is to have select inside a select but if I am trying to return all productions then it will be doing a query for every production.
Just to be clearer. in the second table prodStatus there might be 2 rows with prodID of 4 but the subStatusID for the first one would be 1 and the second one would be 2. The userID will be different. All I want to get back is the second row because it has the highest status number and I need the userID and statusName associated with that row.
I have been googling for 2 days to get this answer and I saw 1 about auctions but I just dont fully understand it even after researching it.
You need to create a subquery which get the maximum value of subStatusID for each prodID.
SELECT a.*, -- select only columns that you want to show
c.*, -- asterisks means all columns
d.*
FROM table1 a
INNER JOIN
(
SELECT prodID, max(subStatusID) maxID
FROM table2
GROUP BY prodID
) b ON a.prodID = b.prodID
INNER JOIN table2 c
ON b.prodID = c.prodID AND
b.maxID = c.subStatusID
INNER JOIN table3 d
ON c.subStatusID = d.subStatusID
I have got 5 tables of which the structures are the same. Only the PAGEVISITS field is unique
ie. table 1:
ITEM | PAGEVISITS | Commodity
1813 50 Griddle
1851 10 Griddle
11875 100 Refrigerator
2255 25 Refrigerator
ie. table 2:
ITEM | PAGEVISITS | Commodity
1813 0 Griddle
1851 10 Griddle
11875 25 Refrigerator
2255 10 Refrigerator
I want it to add up the Commodity to spit out:
table1 | table2 | Commodity
60 10 Griddle
125 35 Refrigerator
Some of the data is actually correct but some are WAY off given the below query:
SELECT
SUM(MT.PAGEVISITS) as table1,
SUM(CT1.PAGEVISITS) as table2,
SUM(CT2.PAGEVISITS) as table3,
SUM(CT3.PAGEVISITS) as table4,
SUM(CT4.PAGEVISITS) as table5,
(COUNT(DISTINCT MT.ITEM)) + (COUNT(DISTINCT CT1.ITEM)) + (COUNT(DISTINCT CT2.ITEM)) + (COUNT(DISTINCT CT3.ITEM)) + (COUNT(DISTINCT CT4.ITEM)) as Total,
MT.Commodity
FROM table1 as MT
LEFT JOIN table2 CT1
on MT.ITEM = CT1.ITEM
LEFT JOIN table3 CT2
on MT.ITEM = CT2.ITEM
LEFT JOIN table4 CT3
on MT.ITEM = CT3.ITEM
LEFT JOIN table5 CT4
on MT.ITEM = CT4.ITEM
GROUP BY Commodity
I believe this may be cause by using the LEFT JOIN incorrectly. I have also tried the INNER JOIN with the same inconsistent results.
I would do a UNION on all five of those tables to get them as one rowset (inline view), and then run a query on that, start with something like this...
SELECT SUM(IF(t.source='MT',t.pagevisits,0)) AS table1
, SUM(IF(t.source='CT1',t.pagevisits,0)) AS table2
, t.commodity
FROM ( SELECT 'MT' as source, table1.* FROM table1
UNION ALL
SELECT 'CT1', table2.* FROM table2
UNION ALL
SELECT 'CT2', table3.* FROM table3
UNION ALL
SELECT 'CT3', table4.* FROM table4
UNION ALL
SELECT 'CT4', table5.* FROM table5
) t
GROUP BY t.commodity
(But I would specify the column list for each of those tables, rather than using the '.*' and having my query dependent on no one adding/dropping/renaming/reordering columns in any of those tables.)
I include an "extra" literal value (aliased as "source") to identify which table the row came from. I can use a conditional test in an expression in the SELECT list, to figure out whether the row came from a particular table.
This approach is particularly flexible, and can be used to get more complicated resultsets. For example, if I also wanted to get a total number page visits from table3, 4 and 5 added together, along with the individual counts.
SUM(IF(t.source IN ('CT2','CT3','CT4'),t.pagevisits,0) AS total_345
To get the equivalent of your COUNT(DISTINCT item) + COUNT(DISTINCT item) + ... expression...
I would use an expression that makes a single value from both the "source" and "item" columns, being careful to have some sort of guarantee that any particular "source"+"item" will not create a duplicate of some other "source"+"item". (If we just concatenate strings, for example, we don't have any way to distinguish between 'A'+'11' and 'A1'+'1'.) The most common approach I see here is a carefully chosen delimiter which is guaranteed not to appear in either value. We can distinguish between 'A::11' and 'A1::1', so something like this will work:
COUNT(DISINCT CONCAT(t.source,'::',t.item))
In your current query, if item is NULL, then the row doesn't get included in the COUNT. To fully replicate that behavior, you would need something like this:
COUNT(DISINCT IF(t.item IS NOT NULL,CONCAT(t.source,'::',t.item),NULL)) AS Total
Or course, getting a count of distinct item values over the whole set of five tables is much simpler (but then, it does return a different result)
COUNT(DISINCT t.item)
But to answer your question about the use of the LEFT JOIN, the left side table is the "driver" so a matching row has to be in that table for a corresponding row to be retrieved from a table on the right. That is, unmatched rows from the tables on the right side will not be returned.
If what you have is basically five "partitions", and you want to process all of the rows whether or not a matching row appears in any of the other "partitions", I would go with the UNION ALL approach to simply concatenate all of the rows from all of those tables together, and process the rows as if they were from a single table.
NOTE: For very large tables, this may not be a feasible approach, since MySQL is going to have to materialize that inline view. There are other approaches which don't require concatenating all of the rows together.
Specifying a list of only the columns you need, in the SELECT from each table, may help performance, if there are columns in those tables you don't need to reference in your query.