GROUP CONCATing multiple rows - mysql

I'm using MySQL 5.1.66 and I came up with a select query to select all the men's shirts and their sizes and prices. I used GROUP CONCAT to merge the size column and price column from their parent tables into the same column. Here's the statement I made
SELECT shirts.shirt_name, shirts.men AS main_photo,
GROUP_CONCAT(shirt_sizes.size_name,shirt_prices.price) AS sizes
FROM shirts
JOIN shirts_link ON shirts_link.shirt_id=shirts.id
JOIN shirt_sizes ON shirt_sizes.id=shirts_link.size_id
JOIN shirt_prices ON shirt_prices.id=shirts_link.price_id
WHERE shirts.men!='' AND shirts_link.adult='y'
GROUP BY shirts.id
By doing this, are the size columns and price columns still two separate entities within that one column in the select statement? Another thing I noticed is the order of the sizes is all mixed up. For example, this is a row from one of the columns
medium29.22,large29.22,1x-large29.22,2x-large30.44,small29.22,3x-large31.70
why isn't it going from small-3x like it's organized on the table? I imagine in the event of what I'm trying to do in terms of injecting it into we website through PHP if it were to auto load it would go in that unorganized manner as well. How can I fix this?
My ultimate goal here is to be able to create a selection that can auto populate each row into the javascript apps I created. I need the load the shirt name, main picture, then each size and price into the divs I have coded for them. Let me know if you need to see the actual tables being used. Thanks in advance :)

First of all purpose of group_concat function is to keep all values of a particular column in a grouped operation. For example,
Table1
ID field1 field2
1 ram science
2 ramesh maths
1 ram maths
If your requirement is to get subjects of a particular person then GROUP_CONCAT will produce then result as follows,
ID field1 field3
1 ram science,maths
2 ramesh maths
Hope this gives some insight

Try this:
SELECT s.shirt_name, s.men AS main_photo,
GROUP_CONCAT(CONCAT(ss.size_name, '.', sp.price)) AS sizes
FROM shirts s
INNER JOIN shirts_link sl ON sl.shirt_id = s.id
INNER JOIN shirt_sizes ss ON ss.id = sl.size_id
INNER JOIN shirt_prices sp ON sp.id = sl.price_id
WHERE s.men!='' AND sl.adult='y'
GROUP BY s.id

Related

MySQL finding all elements that have the same value in column A but different values in cloumn B

I'm fairly new to SQL and have a question regarding a query.
I have a database with various pictures attached to a product. All these pictures have a prediction. The structure is like this:
product_ id picture_id prediction
1------------pic1.jpg----------type a
1------------pic2.jpg----------type b
1------------pic3.jpg----------type b
2------------pic4.jpg----------type a
2------------pic5.jpg----------type a
2------------pic6.jpg----------type a
3------------pic7.jpg----------type c
...
... so on.
Each pictures is predicted individually and because of that some of the products have contradictory predictions (meaning that on the same products some pictures are predicted type a while others are predcited type b).
I want to filter out all of these products with a query. In other words: I need all product_ids where the predictions for the pictures linked to it are not all the same. In our example I want it only to show me product 1.
I tried some stuff with GROUP BY, but have not yet gotten anywhere near the result that I want.
Thanks for helping,
Cheers
Use COUNT(DISTINCT prediction) to get the number of different predictions. It will be more than 1 for the products with different predictions.
SELECT product_id
FROM yourTable
GROUP BY product_id
HAVING COUNT(DISTINCT prediction) > 1

Get a certain query result from two different mysql tables

Im having the following problem:
I try to implement an achievementsystem. I have two tables. Table 1 contains the achievement_id and achievement_info. Table 2 contains the link to the user, meaning achievement_id and player_id, so that you can tell which user has achieved certain things.
I'm trying to write a method that returns me all achievements, but additionally a flag that tells me if a certain user has achieved this row or not.
E.g.: getPlayerAchievements(playerid) --> returns a list of Achievements with id, info, and a bool flag whether the user has achieved it.
table 1:
achievement_id|achievement_info
1 |info1
2 |info2
3 |info3
table 2:
achievement_id|player_id;
1 |15
3 |15
the result I need by entering the player_id "15":
achievement_id|achievement_info|(bool)achieved
1 |info1 |true
2 |info2 |false
3 |info3 |true
I already have the achievement class so I just have to fill them with my data.
I could always use two seperate sql queries to achieve that, but I thought maybe there was a way to simplify it, since I use php to get my data and don't want two connections and queries in one php script.
You want to select all records from the achievemets table and show them. That's the easy part :-) For every record you want to show whether player 1234 has attained this achievement. You can do this with an EXISTS clause:
select
achievement_id,
achievement_info,
exists
(
select *
from players p
where p.player_id = 1234
and p.achievement_id = a.achievement_id
) as achieved
from achievements a;
Or even simpler with IN:
select
achievement_id,
achievement_info,
achievement_id in (select achievement_id from players where player_id = 1234) as achieved
from achievements;
You can use left join to get a complete list of achievements and the matching records from the user's achievements table:
select t1.achievement_id, t1.achievement_info, (t2.achievement_id is null) as achieved
from table1 t1
left join table2 t2 on t1.achievement_id=t2.achievement_id and t2.player_id=15

Querying multiple SQL tables

I'm having trouble understanding joins and subqueries and when to use each. I'm sure that one of them is appropriate here.
I have a table ("owners") of (to keep things simple) unit numbers, names and email addresses.
I have another table ("widgets") of unit numbers and the number of widgets assigned to each unit. Each unit has 0, 1 or 2 widgets.
I need to send an email to each unit depending on whether they have 0, 1 or 2 widgets. In other words (and in plain English, not even remotely an attempt at semi-correct SQL):
select numwidgets from widgets where unit=x
then where owners.unit = widgets.unit
select unit, name, email
The data that I need to pass to my script will look like this:
unit name email widgets
1 Bob Smith bob#example.com 2
I can visualise in my mind the data that I need, but it's extracting it from two different tables that is the problem. The "owners" table is a permanent table, and the "widgets" table is a temporary one for tracking a specific issue that is being addressed in the email I'm sending. I don't need help sending the email, just creating the SQL I need to use to extract the data (numwidgets, name, email) for one email.
Thanks.
EDIT:
Input data:
owners table:
unit, name, email
1,Bob Smith, bob#example.com
widgets table:
unit,widgets
1,2
try this, a inner join selects all rows from both tables as long as there is a match between the columns in both tables.
Subqueries (also known as inner queries or nested queries) are a tool for performing operations in multiple steps. For example, if you wanted to take the sums of several columns, then average all of those values, you’d need to do each aggregation in a distinct step.
select owners.unit, name, email, widgets.numwidgets
from owners
inner join widgets On owners.unit = widgets.unit
where owners.unit = x
For your case you need an inner join. To understand that you need to see the concept of keys which is pretty simple.
In your tables unitnumber is the common column in both tables. So a join has to be applied based on this column.
Subqueries are used when the output of one query is given as input to another query i.e to perform related operations
Select o.unit,o.name,o.email ,w.numwidgets from owners o inner join widgets w on o.unit=w.unit where w.unit=X
In above query pass X = 0,1,2 as per the result you want
Thanks
I think you want:
select o.*, w.widgets
from owners o
inner join widget w
on o.unit = w.unit
where o.unit = 123;

Selecting values where the first 6 characters match

I'm trying to pull a list of IDs from a table Company where the first 6 characters of the ID are the same. The way our application creates a company ID is it takes the first 3 characters of the company name and the first 3 characters of the City. Beceause of that, overtime we have company IDs with the same first 6 characters, followed by a sequential number...
I was thinking using something using LIKE
Select companyID, companyName from Company Where
substring(companyID,1,6)+'%' like substring(companyID,1,6)+'%'
Basically i'm trying to get all company IDs where the first 6 characters match; The result set should show the just the top company ID ( The first 1 created) and the company name. I'm not expecting a tone of results, so i can then use the IDs returned to find the IDs below it.
I'm thinking it could maybe also be done using HAVING, where the count of IDs with the same first 6 characters are the same HAVING Count(*)>1??
Not really sure what the syntax would be...
SELECT distinct c1.CompanyID, c1.CompanyName, c2.CompanyID, c2.CompanyName
FROM dbo.Company c1
JOIN dbo.Company c2
ON SUBSTRING(c1.CompanyName,1,6) = SUBSTRING(c2.CompanyName,1,6)
AND c1.CompanyID < c2.CompanyID
order by c1.CompanyName, c2.CompanyName
SELECT c1.CompanyID, c1.CompanyName, c2.CompanyID, c2.CompanyName
FROM dbo.Company c1
INNER JOIN dbo.Company c2
ON SUBSTRING(c1.CompanyName,1,6) + '%' LIKE SUBSTRING(c2.CompanyName,1,6) + '%'
AND c1.CompanyID <> c2.CompanyID
If this is something that you envision doing frequently, I'd add a computed column to the table that has a definition of substring(CompanyName, 1, 6). You can then index it and make this efficient. As it is, it will have to scan all the entries and calculate the substring on the fly. With the computed column, you amortize the substring calculation up front and at least have a chance at an efficient query.
After trying to use Blam's script, i made a few slight changes and got some better results. His script was returning more results than rows in the table and it was pretty slow; think it's because of the company_name column. I got rid of it and wrote it like this:
select distinct c1.cmp_id, count(substring(c2.cmp_id,1,6)) as TotalCount
from company c1
join company c2 on substring(c1.cmp_id,1,6)=substring(c2.cmp_id,1,6)
group by c1.cmp_id
order by c1.cmp_id asc
This still returns all the table records, but atleast i can see the total count when the first 6 characters are listed more than once. Also, it ran in only 1 second so that's also a plus. Thank again for you input guys, always appreciated!

How do I make the rows of a lookup table into the columns of a query?

I have three tables: students, interests, and interest_lookup.
Students has the cols student_id and name.
Interests has the cols interest_id and interest_name.
Interest_lookup has the cols student_id and interest_id.
To find out what interests a student has I do
select interests.interest_name from `students`
inner join `interest_lookup`
on interest_lookup.student_id = students.student_id
inner join `interests`
on interests.interest_id = interest_lookup.interest_id
What I want to do is get a result set like
student_id | students.name | interest_a | interest_b | ...
where the column name 'interest_a' is a value in interests.name and
the interest_ columns are 0 or 1 such that the value is 1 when
there is a record in interest_lookup for the given
student_id and interest_id and 0 when there is not.
Each entry in the interests table must appear as a column name.
I can do this with subselects (which is super slow) or by making a bunch of joins, but both of these really require that I first select all the records from interests and write out a dynamic query.
You're doing an operation called a pivot. #Slider345 linked to (prior to editing his answer) another SO post about doing it in Microsoft SQL Server. Microsoft has its own special syntax to do this, but MySQL does not.
You can do something like this:
SELECT s.student_id, s.name,
SUM(i.name = 'a') AS interest_a,
SUM(i.name = 'b') AS interest_b,
SUM(i.name = 'c') AS interest_c
FROM students s
INNER JOIN interest_lookup l USING (student_id)
INNER JOIN interests i USING (interest_id)
GROUP BY s.student_id;
What you cannot do, in MySQL or Microsoft or anything else, is automatically populate columns so that the presence of data expands the number of columns.
Columns of an SQL query must be fixed and hard-coded at the time you prepare the query.
If you don't know the list of interests at the time you code the query, or you need it to adapt to changing lists of interest, you'll have to fetch the interests as rows and post-process these rows in your application.
What your trying to do sounds like a pivot.
Most solutions seem to revolve around one of the following approaches:
Creating a dynamic query, as in Is there a way to pivot rows to columns in MySQL without using CASE?
Selecting all the attribute columns, as in How to pivot a MySQL entity-attribute-value schema
Or, identifying the columns and using either a CASE statement or a user defined function as in pivot in mysql queries
I don't think this is possible. Actually I think this is just a matter of data representatioin. I would try to use a component to display the data that would allow me to pivot the data (for instance, the same way you do on excel, open office's calc, etc).
To take it one step further, you should think again why you need this and probably try to solve it in the application not in the database.
I know this doesn't help much but it's the best I can think of :(