stuck on how to do a specific query - mysql

I have three tables, items, values, and properties, their structure....
Items
id
title
desc
timestamp
Values
id
value
itemID
propID
Properties
id
name
desc
timestamp
Essentially, with this structure I can create an item, and then assign it any property or value. But I'm running into an issue of how I can search for existing items based on properties and values.
Such that, if I had an item, with a composite key dumped into this, if it were a normal table, I could so "SELECT * FROM items WHERE key1 = 'val1' AND key2 = 'val2'", but I can't figure out how to emulate a search like that, with this kind of structure. Does anyone else have any ideas?
This is for a system I was thrown into, so changing the layout of the tables may not be something I'll be given permission to do.
EDIT: I could break this into multiple steps, but the solutions I'm currently looking at will be slow and could becoming somewhat memory intensive, given the size of these data collections.

I would use an INNER JOIN query
SELECT *
FROM Items a
INNER JOIN Values b
ON a.id = b.itemID
INNER JOIN Propeties c
ON b.propID = c.id
WHERE c.title = 'height' AND b.value = '55'
Now in where you can place any of your column table, they are all joine now, just remember to preoend table alias (a,b,c) to your column name according

If you want to avoid joins you can create another table which will have three columns Item_Id, Property_Id and Value_Id. In this table you can search any thing on the basis of Id.
OR
You can do this:
SELECT * FROM Items WHERE IN (SELECT itemID FROM Values WHERE value=SOMEVALUE)

Related

Combine a single table IF query with a multi-table join

Sorry if the title was sort of vague was unsure how to phrase it.
I currently am choosing values for a field from table like so (clickable SqlFiddle link included):
`Select `ID`,IF(`SpecificationID` is NULL,'0', `SpecificationID`) from Orders;`
http://www.sqlfiddle.com/#!9/b8108b/8
Which as you can see pulled the SpecificationID from the Orders table and if it found a NULL pulled 0. I get that I could/should make the default values in my table = 0 yet to do so would require multiple code changes so this was the easiest way to pull 0 for NULL values which I ultimately need for this process.
It turned out that Orders can have multiple Specifications so I created a Look-Up-Table with OrderID and SpecificationID
My new query therefor looks liks this:
Select `OrderID`,`SpecificationID` from LUT_Orders_Specifications
order by OrderID;
http://www.sqlfiddle.com/#!9/b8108b/9
The issue is that since the LUT only stores values for records with an OrderID I don't get OrderIDs which have no values and therefore don't get 0 for them.
I am unsure how/if I can do a Join that will get the values from the LUT and also return 0 for the records that are not in the LUT.
I know how to select IDs that are not in a table e.g.
Select ID from Orders where ID not in
(Select T1.OrderID from LUT_Orders_Specifications T1
Inner Join Orders T2
on T1.OrderID=T2.ID)
http://www.sqlfiddle.com/#!9/b8108b/15
But I am unclear how I can combine the processes so I am grabbing all the specification values for a single order and a 0 value for an order that has no specifications.

SQL query for selecting all but needing aliases

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.

How to deal with bad data in mysql?

I have three tables that I want to combine.
I have the following query to run:
DROP TABLE
IF EXISTS testgiver.smart_curmonth_downs;
CREATE TABLE testgiver.smart_curmonth_downs
SELECT
ldap_karen.uid,
ldap_karen.supemail,
ldap_karen.regionname,
smart_curmonth_downs_raw.username,
smart_curmonth_downs_raw.email,
smart_curmonth_downs_raw.publisher,
smart_curmonth_downs_raw.itemtitle,
smart_items.`Owner`
FROM
smart_curmonth_downs_raw
INNER JOIN ldap_karen ON smart_curmonth_downs_raw.username = ldap_karen.uid
INNER JOIN smart_items ON smart_curmonth_downs_raw.itemtitle = smart_items.Title
I want to know how to create the joins while maintaining a one to one relationship at all times with rows in table smart_curmonth_downs_raw.
For instance if there is not a uid in ldap_karen I have issues. And then the last issue I have found is that our CMS is allowing for duplicate itemtitle. So if I run my query I am getting a lot more rows because it is creating a row for each itemtitle. For example would there be a way to only catch the last itemtitle that is in smart_items. I would just really like to maintain the same number of rows - and I have no control over the integrity issues of the other tables.
The smart_curmonth_downs_raw table is the raw download information (download stats), the karen table adds unique user information, and the smart_items table adds unique items (download) info. They are all important. If a user made a download but is knocked off the karen table I would like to see NULLs for the user info and if there is more than one item in smart_items that has the same name then I would like to see just the item with the highest ID.
It sounds like relationship between smart_curmonth_downs_raw and ldap_karen is optional, which means you want to use a LEFT JOIN which all the rows in the first table, and, if the right table does not exists, use NULL as the right table's column values.
In terms of the last item in the smart_items table, you could use this query.
SELECT title, MAX(id) AS max_id
FROM smart_items
GROUP BY title;
Combining that query with the other logic, try this query as a solution.
SELECT COALESCE(ldap_karen.uid, 'Unknown') AS uid,
COALESCE(ldap_karen.supemail, 'Unknown') AS supemail,
COALESCE(ldap_karen.regionname, 'Unknown') AS regionname,
smart_curmonth_downs_raw.username,
smart_curmonth_downs_raw.email,
smart_curmonth_downs_raw.publisher,
smart_curmonth_downs_raw.itemtitle,
smart_items.`Owner`
FROM smart_curmonth_downs_raw
INNER JOIN (SELECT title, MAX(id) AS max_id
FROM smart_items
GROUP BY title) AS most_recent
ON smart_curmonth_downs_raw.itemtitle = most_recent.Title;
INNER JOIN smart_items
ON most_recent.max_id = smart_items.id
LEFT JOIN ldap_karen
ON smart_curmonth_downs_raw.username = ldap_karen.uid;

Join on 3 tables insanely slow on giant tables

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.

Storing an array of id's in one db field VS breaking out into a joining table - what sql to use?

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.