I'm finding that SQL related questions are very difficult to express conversationally so please forgive me if this makes no sense.
I'm migrating data out of a CMS database to MySQL and I would like to simplify the data structure wherever possible. The current category scheme has 7 categories and has an item to category relationship setup as a many-to-many so a junction table is required.
It would be better setup as two one-to-many relationships so that an extra table is not required. The actual category setup that I need is as follows:
Category 1
Option 1
Option 2
Category 2
Option 3
Option 4
Option 5
Option 6
Option 7
Each item belongs to one value for Category 1, and one value for Category 2. This is the general form of the query I'm creating:
SELECT items.entry_id, categories.cat_id
FROM items
INNER JOIN categories
ON items.entry_id = categories.entry_id
WHERE items.item_type = 6
How would I add a conditional or sub query so that I got the results of the SELECT clause like:
SELECT items.entry_id, categories.cat_id1, categories.cat_id2
where the value of cat_id1 and cat_id2 are the values described above?
****Update****
I have made some progress getting the query I need (the tables are too complicated to post here for examples but here is a sample query):
SELECT exp_weblog_data.entry_id,
exp_weblog_data.field_id_27 AS Title,
exp_weblog_data.field_id_29,
exp_weblog_data.field_id_32,
exp_weblog_data.field_id_33,
exp_weblog_data.field_id_28,
exp_weblog_data.field_id_84,
exp_relationships.rel_child_id,
CASE WHEN exp_category_posts.cat_id = '15' OR exp_category_posts.cat_id = '16' THEN exp_category_posts.cat_id END as cat1,
CASE WHEN exp_category_posts.cat_id = '17' OR exp_category_posts.cat_id = '20' THEN exp_category_posts.cat_id END as cat2
FROM exp_weblog_data
INNER JOIN exp_relationships
ON exp_weblog_data.entry_id = exp_relationships.rel_parent_id
INNER JOIN exp_category_posts
ON exp_weblog_data.entry_id = exp_category_posts.entry_id
WHERE exp_weblog_data.weblog_id = 6
This gets me the two columns I want for Cat1 and Cat 2 but there are still two problems here - the inner join on exp_category_posts is resulting in 2 rows for each record where I only want one row for each value of entry_id. Secondly, in the case statements, I want to set a value of A if cat_id = 15 and B if cat_id = 16 but I can't seem to find the right syntax for this without getting errors.
I hope this clears things up a bit!
There is no reasonable way to avoid an extra table when setting up a many-to-many relationship.
Do not have multiple columns for multiple categories (cat_id1, cat_id2, ...).
Do not put multiple ids in a single column ("123,234,345").
Both of those lead to problems that are worse than having the mapping table.
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.
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;
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'
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
I have a query that returns results related to items that match a specific category...
There are 3 mysql tables that results to this, items, categories and item_categories.
These i assume are self explanatory, but the latter, is a linking table that links any specific item to any specific category, using a match of id's.
The items table contains one row, with an id value of 1.
The categories table is filled with 15 rows, with id values of 1-15.
the item_categories table contains one row, the item_id value is 1 and the category_id value is 5.
This is the mysql query in its php form:
$catResultQuery = "
SELECT i.id, name, price
FROM items i
INNER JOIN item_categories
ON i.id = item_id
INNER JOIN categories c
ON category_id = c.id
WHERE MATCH (c.id)
AGAINST ('{$_SESSION['input']}' IN BOOLEAN MODE)
ORDER BY name
";
The session variable has a value of 5, but for some reason, this query displays a 0 result set.
Even when i run the query in php myadmin, it returns 0 rows.
And i am confused, because in my head, the logic behind all of this seems fairly simple, but for some reason i get 0? Does anyone have any idea where i have gone wrong with this?
Any advice and input would be greatly appreciated, thank you!!
Ok, I see now that you're building the SQL dynamically. If that's the case, then this should work:
SELECT i.id, name, price
FROM items i
INNER JOIN item_categories
ON i.id = item_id
INNER JOIN categories c
ON category_id = c.id
WHERE c.id
IN ('{$_SESSION['input']}')
ORDER BY name
Just make sure '{$_SESSION['input']}' is comma delimited and be aware that this carries the risk of SQL injection because you're constructing the SQL on the fly.