Restricting sub-query to 2 arguments in same row - mysql

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

Related

How to use join instead of select using mysql?

i want to find the sum of the items for the comp_id = 2
what i am trying to do?
i have two tables namely purchaseditems and item. both have comp_id in common. now i have to find the sum of items for the comp_id = 2.
below is how the tables look
As seen from above table i am expecting the sum to be 13.
so i have used the query below to get the sum of items for comp_id = 2.
SELECT sum(item.count) as sum FROM item, purchaseditems WHERE item.id = purchaseditems.item_id AND
purchaseditems.comp_id= 2;
How can i do the same using JOIN i am new to using mysql..still a beginner in this.
could someone help me fix this. thanks
You can use INNER JOIN to find the common rows between two tables matching a common column value. The below query should work for you:
SELECT sum(item.count) FROM item JOIN purchasedItems ON item.id = purchasedItems.item_id where purchasedItems.comp_id = 2
You may read about various types of joins

SQL Query - Distinct on One Column for Distinct Value of Other (with INNER JOIN)

I appreciate that questions similar to this one have been asked on here before but I have thus far been unable to implement the answers provided into my code both because of wanting to distinguish duplicates in one column only whilst the other stays the same and the INNER JOIN in my code. The INNER JOIN is problematic because most of the provided answers use the PARTITION function and, being a novice with SQL, I do not know how to integrate this with it. Advice just on using INNER JOIN with PARTITION would be useful.
Whilst I could do this post-export in Python (where I will be using the desired output), this code currently outputs ~2 million rows, making it time-consuming to work with and check. Here is the code:
SELECT client_ip_address, language_enum_code
FROM vw_user_session_log AS usl
INNER JOIN vw_user_topic_ownership AS uto
ON usl.user_id = uto.user_id
Using SELECT DISTINCT instead of SELECT gets me closer to the desired output but rather than leaving one duplicate row behind it removes all of them. Advice on using this function whilst preserving one of the duplicate rows would be preferred. I am on a read-only connection to the database so the DELETE FROM approach seen here would only be viable if I could make a temporary query-able table from the query output which I don't think is possible and seems clumsy.
Raw data sample:
user_id: client_ip_address: language_enum_code: (other stuff...)
4 194:4:62:18 107
2 101:9:23:34 14
3 180:4:87:99 15
3 194:4:62:18 15
4 166:1:19:27 107
2 166:1:19:27 14
Desired result:
user_id: client_ip_address: language_enum_code: (other stuff...)
4 194:4:62:18 107
2 101:9:23:34 14
3 180:4:87:99 15
As you can see, any id-enum combination should be filtered to occur only once. The reason this is not any ip-enum combination is that multiple users can connect through the same IP address.
If you don't care which IP address you keep for each user_id / enum combo, then something like this should do:
SELECT user_id, min(client_ip_address), language_enum_code
FROM vw_user_session_log AS usl
INNER JOIN vw_user_topic_ownership AS uto
ON usl.user_id = uto.user_id
where client_ip_address is not null
group by user_id, language_enum_code
Do you simply want aggregation?
SELECT client_ip_address, GROUP_CONCAT(DISTINCT language_enum_code)
FROM vw_user_session_log usl INNER JOIN
vw_user_topic_ownership uto
ON usl.user_id = uto.user_id
GROUP BY client_ip_address;
This will return one row per client_ip_address with each language code in a comma delimited list.
You can also use MIN() or MAX() to get an arbitrary value for language_enum_code for each client_ip_address.

MySQL query for multiple table data

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.

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.

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