So I have this three tables:
product (p)
product_has_component (phc)
product_component (pc)
Product have multiple components, at least one, and table phc store info how the joining should go. So, I can check which components given product has - that is easy.
But now I want to do opposite check: after selecting multiple components, I want to know every product, that posses all of them.
I made this query:
SELECT DISTINCT phc.product_id
FROM product_has_component phc
JOIN product_component pc
ON pc.id = phc.component_id
AND (pc.component_character = 'A' OR pc.component_character = 'y')
And it returns products id numbers, that have either 'A' OR 'y' component.
But how can I get those having 'A' AND 'y' components?
Obviously, replacing OR with AND will return nothing, so I guess this whole query's structure is just incorrect.
I also thought about doing something like select all with 'A', then select all with 'y' and then try to find common produts, but this seem to be very ineffective solution.
Related
I'm trying to perform a join operation on two tables linked by item IDs. However, the problem with them is that they've got columns with the same name as follows:
items
(ID, **Quantity**, etc. /*[nothing in etc. is shared by status' columns]*/)
status
(ID, **Quantity**, etc. /*[nothing in etc. is shared by items' columns]*/)
I want to get all records from these tables and join them, but I don't know what the SQL query would look like. I know it'd be something like:
SELECT *
FROM items
LEFT OUTER JOIN status
ON items.ID = status.ID
and I know I need aliases for the two quantity columns (which I know how to do), but where does the latter part of the query fit in?
In general, I recommend avoiding SELECT * queries. Just select the specific columns you need, and if there are duplicate column names you can easily assign aliases for them.
SELECT i.col1, i.col2, i.quantity AS item_quantity, s.col3, s.col4, s.quantity AS status_quantity
FROM items AS i
JOIN status AS s ON i.ID = s.ID
But if you really need to select all columns, you can use the solution in Marc B's answer.
I have a query which goes like this:
SELECT insanlyBigTable.description_short,
insanlyBigTable.id AS insanlyBigTable,
insanlyBigTable.type AS insanlyBigTableLol,
catalogpartner.id AS catalogpartner_id
FROM insanlyBigTable
INNER JOIN smallerTable ON smallerTable.id = insanlyBigTable.catalog_id
INNER JOIN smallerTable1 ON smallerTable1.catalog_id = smallerTable.id
AND smallerTable1.buyer_id = 'xxx'
WHERE smallerTable1.cont = 'Y' AND insanlyBigTable.type IN ('111','222','33')
GROUP BY smallerTable.id;
Now, when I run the query first time it copies the giant table into a temp table... I want to know how I can prevent that? I am considering a nested query, or even to reverse the join (not sure the effect would be to run faster), but that is well, not nice. Any other suggestions?
To figure out how to optimize your query, we first have to boil down exactly what it is selecting so that we can preserve that information while we change things around.
What your query does
So, it looks like we need the following
The GROUP BY clause limits the results to at most one row per catalog_id
smallerTable1.cont = 'Y', insanelyBigTable.type IN ('111','222','33'), and buyer_id = 'xxx' appear to be the filters on the query.
And we want data from insanlyBigTable and ... catalogpartner? I would guess that catalogpartner is smallerTable1, due to the id of smallerTable being linked to the catalog_id of the other tables.
I'm not sure on what the purpose of including the buyer_id filter on the ON clause was for, but unless you tell me differently, I'll assume the fact it is on the ON clause is unimportant.
The point of the query
I am unsure about the intent of the query, based on that GROUP BY statement. You will obtain just one row per catalog_id in the insanelyBigTable, but you don't appear to care which row it is. Indeed, the fact that you can run this query at all is due to a special non-standard feature in MySQL that lets you SELECT columns that do not appear in the GROUP BY statement... however, you don't get to select WHICH columns. This means you could have information from 4 different rows for each of your selected items.
My best guess, based on column names, is that you are trying to bring back a list of items that are in the same catalog as something that was purchased by a given buyer, but without any more than one item per catalog. In addition, you want something to connect back to the purchased item in that catalog, via the catalogpartner table's id.
So, something probably akin to amazon's "You may like these items because you purchased these other items" feature.
The new query
We want 1 row per insanlyBigTable.catalog_id, based on which catalog_id exists in smallerTable1, after filtering.
SELECT
ibt.description_short,
ibt.id AS insanlyBigTable,
ibt.type AS insanlyBigTableLol,
(
SELECT smallerTable1.id FROM smallerTable1 st
WHERE st.buyer_id = 'xxx'
AND st.cont = 'Y'
AND st.catalog_id = ibt.catalog_id
LIMIT 1
) AS catalogpartner_id
FROM insanlyBigTable ibt
WHERE ibt.id IN (
SELECT (
SELECT ibt.id AS ibt_id
FROM insanlyBigTable ibt
WHERE ibt.catalog_id = sti.catalog_id
LIMIT 1
) AS ibt_id
FROM (
SELECT DISTINCT(catalog_id) FROM smallerTable1 st
WHERE st.buyer_id = 'xxx'
AND st.cont = 'Y'
AND EXISTS (
SELECT * FROM insanlyBigTable ibt
WHERE ibt.type IN ('111','222','33')
AND ibt.catalog_id = st.catalog_id
)
) AS sti
)
This query should generate the same result as your original query, but it breaks things down into smaller queries to avoid the use (and abuse) of the GROUP BY clause on the insanlyBigTable.
Give it a try and let me know if you run into problems.
I have concluded that my first quick fix of storing an array of ids in a singular database field (1,5,48) is probably not best but if I break them out into a joining table of 'parent item id' (singular) and then sub related item id (multiple) which links to a separate table it would be better.
But now I am unsure what mysql query to use to get matches.
So a search form is submitted where "related_item" array is "1,5,8" and one of my "parent_items" has related item matches of "1" and "8" in the joining table....
So what mysql query would return these matches?
UPDATE:
I have one table 'companies' maybe HSBC and TOPSHOP as example records.
There is a separate table of 'industries' maybe 'banking' and 'retail'
There is a joining table which is company_id and industry_id which pairs them both together
So if someone submits a search form for where industry = 'banking' or 'retail' how would I return the company records for 'topshop' and 'hsbc'
Something like this... its unclear to me form you description if you are "searching" children or parents. I think you mean to search children so:
-- SEARCH CHILDREN --
SELECT p.*, c.* FROM child_table c
LEFT JOIN linking_table l ON (p.id = l.child_table_id)
LEFT JOIN parent_table p ON (l.parent_item_id = p.id)
WHERE c.id IN (1,5,4,8)
-- OPTIONAL AND CLAUSE for p.id = ? --
If you mean to search parents then you can just rework the order, although you could use the exact same query with the exception that your WHERE IN clause would be on p.id instead of c.id.
Be aware though that this will get you 1 row per match for the children so you will have the same parent multiple times. If you dont need the actual child data then you could use DISTINCT to only return one instance.
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
Ok, here's a fun one. I have 2 tables: tbl_notes, tbl_notes_categories
Simply, the tbl_notes has a categoryid, and I correlate the 2 tables with that ID. So, nothing too complicated.
I force users to choose a category, from a dropdown input, and stop them from submitting if they don't select something.
However, I want to change this, primarily for learning JOINs and how far I can go with them.
Sooooooo, I am not going to force a user to select a category, and instead, I will default the categoryid to zero, in the tbl_notes. (most users will select a category, but this is for other instances)
In the query, I am locked to showing only the notes that have a categoryid that exists in the tbl_notes_categories table. But, I would like to have a condition if the categoryid is not recognized OR is equal to zero, then specify another String. Like "--Unassigned--", or "--Category does not exist--"
Here's my original query:
SELECT n.notesubject, c.categoryname
FROM `tbl_notes` n, `tbl_notes_categories` c
WHERE n.categoryid = c.categoryid
This will not let me see the notes without a categoryid, so I pulled this one:
SELECT n.notesubject, c.categoryname
FROM `tbl_notes` n
LEFT JOIN `tbl_notes_categories` c ON n.categoryid = c.categoryid
And that helps, but I'm stuck at the 'condition' of displaying alternate text, in the case of a missing category record from the categories table.
In MySQL you can use IFNULL:
SELECT
n.notesubject,
IFNULL(c.categoryname, 'Unknown') AS categoryname
FROM tbl_notes AS n
LEFT JOIN tbl_notes_categories AS c
ON n.categoryid = c.categoryid
This will work if the category is not found, but it will also work if the category id is zero assuming that you don't have a matching row in your category table because then it will also not be found. If for some reason you do want a row in the categroy table with id zero then you can just set its name to 'Unknown'.
Note that IFNULL is MySQL specific. The function COALESCE will also work and is supported by more databases.
For IF/ELSE statements in general in MySQL can use IF or for a more general solution use a CASE expression: CASE WHEN condition THEN expr1 ELSE expr2 END.