So I have a table full of cars:
Say there are 4 dodge vipers, there would be 4 entries in the database, all are identical apart from the carID.
carID | carname | carmodel | colour | reg
--------------------------------------------------
1 | viper | dodge | red | 123
2 | viper | dodge | red | 124
3 | viper | dodge | red | 125
4 | viper | dodge | red | 126
5 | R8 | audi | blue | 127
6 | R8 | audi | blue | 128
When a User searches for cars, I want to display only one dodge viper. However I want to pull all the info from that row, and every other distinct car.
So the output I desire is:
carID | carname | carmodel | colour | reg
--------------------------------------------------
1 | viper | dodge | red | 123
5 | R8 | audi | blue | 127
If I do:
SELECT DISTINCT * FROM cars
it pulls back all entries.
SELECT DISTINCT carname FROM cars
Only pulls back one of each, but I only have the car name.
Is there such a:
SELECT * FROM cars ORDER BY DISTINCT carname
Or something similar?
If you want to return he carId, then you can use the following which will return the min(carid) for each identical carname, etc:
select min(carId) as carId, carname, carmodel, colour
from cars
group by carname, carmodel, colour
See SQL Fiddle with Demo
You will see that I placed the carId in an aggregate function this is to make sure that MySQL will always return the expected value for the carId column. When you do not GROUP BY or aggregate the items in the SELECT list, you might return unexpected results. (see MySQL Extensions to GROUP BY)
From the MySQL Docs:
MySQL extends the use of GROUP BY so that the select list can refer to nonaggregated columns not named in the GROUP BY clause. ... You can use this feature to get better performance by avoiding unnecessary column sorting and grouping. However, this is useful primarily when all values in each nonaggregated column not named in the GROUP BY are the same for each group. The server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate. Furthermore, the selection of values from each group cannot be influenced by adding an ORDER BY clause. Sorting of the result set occurs after values have been chosen, and ORDER BY does not affect which values the server chooses.
I don't understand, do you mean grouping ?
SELECT MAX(CarId) as carId, carname, carmodel, colour FROM cars GROUP BY carname, carmodel, colour;
Try this:
SELECT DISTINCT carname, otherCol1, otherCol2... FROM cars
SQLFiddle Here
As You editted the question and want to display your car_id, above solution is no good.
Here you go:
SELECT MIN(carId) AS carId, carname, carmodel, colour
FROM cars
GROUP BY carname, carmodel, colour
MIN or MAX SUM Whatever you use is just a projection...perception you can say
Related
I am quite new to MySql (3 months of YouTube tutorials) and most of my experience comes from PHP/HTML(4-5 years building websites) and I am trying to build a report for some data that I've started collecting.
Any help you can provide is greatly appreciated!
I have a MySql database that collects multiple columns of materials/products that correspond to a quantity of material that was used on a construction site.
The data currently look like this:
Material1 | Quantity1 | Material2 | Quantity2 | Material3 | Quantity3|
Concrete Bags| 35 | Hydroseed | 1300 | Diesel Fuel | 40 |
Straw Wattles| 32 | Wooden Stakes| 200 | Diesel Fuel | 30 |
Hydroseed | 1000 | Wooden Stakes| 100 | Diesel Fuel | 20 |
What query or process can I use to add the quantities of same name materials, and combine the material names?
Hydroseed has two entries, 1300 + 1000 = 2300; Diesel Fuel has three entries, 40 + 30 + 20 = 90; etc. Material1, Material2, Material3 = MaterialName
I want to display it in the following manner:
MaterialName | Quantity |
Concrete Bags | 35 |
Hydroseed | 2300 |
Straw Wattles | 32 |
Wooden Stakes | 300 |
Diesel Fuel | 90 |
I am not exactly sure how to approach this.
My current query looks like this:
("SELECT Material1 AS MaterialName FROM table UNION SELECT Material2 AS MaterialName FROM table UNION SELECT Material3 AS MaterialName FROM database GROUP BY MaterialName");
I do not know what to do with the Quantities - I do not know how to combine the two queries and have the totals match up with the correct material name - perhaps something of this sort:
("SELECT *, SUM(Quantity1 + Quantity2 + Quantity3) AS Quantity FROM table GROUP BY MaterialName");
You may do an aggregation over a union of the three types of column data:
SELECT material AS MaterialName, SUM(quantity) AS Quantity
FROM
(
SELECT Material1 AS material, Quantity1 AS quantity FROM yourTable UNION ALL
SELECT Material2, Quantity2 FROM yourTable UNION ALL
SELECT Material3, Quantity3 FROM yourTable
) t
GROUP BY material;
The basic strategy here is to bring the three pairs of material data into just two columns. Then, aggregate by material and find the sum of quantity, for each material.
Demo
By the way, your current data model is not good, and in general you should not be storing the same logical thing across multiple columns. Consider just having two columns, one for the material and the other for the quantity.
I have a table with records of cars. I want to query that table so that all (found) records are retrieved AND a table with the district-name plus how many cars are in the districts is shown besides the results.
Like a normal craigslist result page or similiar sites, where the results are shown, and on the left side is a table which shows where (in which disctrict, etc.) the results are and how many in each district are, etc.
At the moment I achieve that with two queries, but I asked me if that couldn't be done with just one query?
My table looks simplified like that:
name | color | year | district
------------------------------
ford | blue | 1998 | 1010
opel | red | 2001 | 1030
vw | green | 1999 | 1010
... | ... | ... | ...
So the result should look like:
districts results
--------- ----------
1010 (2) ford, 1998
1030 (1) opel, 2001
.... vw, 1999
...
Hope you know what I mean.
You can't generate two views from a single query. If you want to view all records, that's one query. If you want to view records grouped by district, that's a separate query.
You can't have two result sets generated from one query.
To get the district name with the total number of cars do this:
SELECT d.name, COUNT(c.id) as total
FROM districts AS d
LEFT JOIN cars AS c ON c.district = d.id
GROUP BY d.id;
My guess is that you have to use GROUP BY for this.
http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html
SELECT c.name,
c.color,
c.year,
c.district,
COUNT(c.id) counted,
FROM cars c
GROUP BY district, name
So I have this query:
SELECT * FROM cars {$statement} AND deleted = 'no' AND carID NOT IN (SELECT carID FROM reservations WHERE startDate = '".$sqlcoldate."') GROUP BY model
It basically checks the reservations table and then if there are reservations, it gets those carIDs and excludes them from the loop.
This is cool, so as there may be three dodge vipers and 2 are booked out it will only display the last one, and it will only display one at a time anyway because I group the results by model.
All that is good, however when it runs out of entries, so all the cars are booked out, the car does not appear in the list of cars. (As i clear from the query).
I would like a way to say if no rows of a certain car model are in the results, to display a placeholder, that says something like 'UNAVAILABLE'.
Is this possible at all? Its mainly so users can see the company owns that car, but knows its not available on that date.
You should probably handle this in the PHP, checking the number of rows returned and replacing the 0 with "UNAVAILABLE".
Based on TO comment:
In this case you want to look at
http://dev.mysql.com/doc/refman/5.1/en/case.html
This would need to go into the SELECT list like
SELECT
CASE car_count WHEN 0 THEN 'UNAVAILABLE'
WHERE ...
Without seen some of your data, its hard to give you a query, but if you move your subquery to your select expression, you could return the count available (which would be 0 when they are all reserved). Then when you display your data, you could then check if the count is 0, and display your unavailable message.
Edit:
Given the table cars:
+----+----------+
| id | model |
+----+----------+
| 1 | viper |
| 2 | explorer |
| 3 | viper |
| 4 | explorer |
+----+----------+
and the table reservations:
+-------+------------+
| carid | date |
+-------+------------+
| 1 | 2013-03-07 |
| 3 | 2013-03-07 |
+-------+------------+
A query similar to yours above will return:
+----+----------+
| id | model |
+----+----------+
| 2 | explorer |
+----+----------+
If you change it to something like:
SELECT
`outer`.`model`,
(
SELECT COUNT(*)
FROM
`cars` AS `inner`
WHERE
`inner`.`model` = `outer`.`model` AND
`inner`.`id` NOT IN(
SELECT `carid`
FROM `reservations`
WHERE `date` = '2013-03-07'
)
GROUP BY `inner`.`model`
) AS `count`
FROM cars AS `outer`
GROUP BY `outer`.`model`;
then you would get results like:
+----------+-------+
| model | count |
+----------+-------+
| explorer | 2 |
| viper | NULL |
+----------+-------+
If you then needed the NULL value to come back as a 0, you could use COALESCE, as Liv mentioned previously.
It's not pretty, and I'm sure it could be done a much cleaner way, but it does work.
There was a similar question asked here that might get you headed in the right direction. Check out the COALESCE() function.
The built-in function COALESCE() returns the first not-null value in its arguments. This lets you structure queries like SELECT COALSECE(foo, 'bar') [...] such that the result will be the value in column 'foo' if it is not null, or the value 'bar' if it is.
I need some help with a group by mysql query clause.
Medals Table (this holds all the medals):
+-----------------------------------------------------------------------+
| medal_id | medal_level | medal_name | medal_type | medal_icon |
|-----------------------------------------------------------------------|
| 1 | 1 | 1 post medal | 1 | icon_file.png |
| 2 | 2 | 1 thread medal | 2 | icon_file.png |
| 3 | 1 | 2 post medal | 1 | icon_file.png |
| 4 | 2 | 2 threads medal | 2 | icon_file.png |
+-----------------------------------------------------------------------+
Users Medals Table (this holds the medals which users have won):
+--------------------------------+
|medal_id |user_id | earnedtime |
|--------------------------------|
| 1 | 1 | 1313548360 |
| 2 | 1 | 1313548365 |
| 3 | 1 | 1313548382 |
| 4 | 1 | 1313548410 |
+--------------------------------+
MySQL Query:
SELECT m.*, u.*
FROM users_medals u
LEFT JOIN medals m ON (u.medal_id=m.medal_id)
WHERE u.user_id IN(1)
GROUP BY m.medal_type
ORDER BY u.earnedtime
What this is intended to do is display medals users have earned (this is a plugin for a bulletin board system). It selects and displays the medals where the users medal id is equal to the medal id in the table that holds all the medals.
This works fine, however, it's not displaying the latest medal. It's only displaying the following medal id's: 1, 2. It should be displaying 3 and 4.
Additional Info: I only want to display one medal from each medal type. So for example, if the user has earned two "post medals", only the latest one earned will be displayed, along with any other medals earned.
Any help would be greatly appreciated.
SELECT m.*, u.*
FROM users_medals u
LEFT JOIN medals m ON u.medal_id = m.medal_id
WHERE u.user_id IN (1)
AND u.earnedtime = (
SELECT MAX(users_medals.earnedtime) FROM users_medals
LEFT JOIN medals ON users_medals.medal_id = medals.medal_id
WHERE users_medals.user_id = u.user_id
AND medals.medal_type = m.medal_type
)
I think it should be possible to do this as a subjoin too but the restriction of the medal type being in another table made my head ache. It would probably be easier to write if there was a view of the two tables joined together.
You have "GROUP BY medal_type". Since your medals table only has values of 1,2 for this column, you won't get 3 & 4 in your results.
It's not showing 3 and 4 because you are grouping it by medal type. So 3 and 4 and the same type as 1 and 2. If you take group by off it will show every medal.
If you want this group by but only want 3 and 4. Then then you simply need to
order by m.medal_type descending
Since you're grouping by the medal type, it'll only select one of each type. If you want a list of all the medals a user got, why are you grouping by the medal type?
Perhaps you're looking for the highest level of the medal by type? If that's the case, you might want to specify that.
Dalen did ask the right thing - why would you like to use GROUP BY in the first place? That's not needed in this query. GROUP BY serves a different purpose; maybe you can understand it more by checking an example like this one.
i have a form that has a multiple select drop down. a user can select more than one options in the select. the name of the select is array[]; using php I call implode(",",$array)
in mysql db, it stores the field as a text in this format "places"= "new york, toronto, london" when i want to display these fields i explode the commas.
I am trying to run a report to display the places. here is my select:
"select * from mytable where db.places .. userSelectedPlaces"
how can i check toronto in lists of "places" that user selected? note "places" in the db might be either just "toronto" or it might be comma separated lists of places like "ny, toronto, london, paris, etc".
If it is possible, you would be much better off using another table to hold the places that the user has selected. Call it SelectedPlaces with columns:
mytable_id - To join back to the table in your query
place - EG: "Toronto"
Then you can run a simple query to figure out if Toronto has been selected:
SELECT *
FROM mytable m
INNER JOIN SelectedPlaces sp ON sp.mytable_id = m.id
WHERE sp.place = 'Toronto'
If I understand you correctly, your database design is just wrong. Try reading about it more. Generally, in good design you should not have lists of values as one field in database and you should introduce new table for it.
But if you want to do it this way, you can use strcmp function.
If i understood correctly, this should work:
WHERE DB.PLACES LIKE '%TORONTO%'
but as other users said, its not a nice thing to have denormalized tables.
To directly answer your question, your query needs to look something like this
SELECT *
FROM mytable
WHERE places LIKE( '%toronto%' )
But, be aware, that LIKE() is slow.
To indirectly answer your question, your database schema is all wrong. That is not the right way to do a M:N (many-to-many) relationship.
Imagine instead you had this
mytable place mytable_place
+------------+ +----------+----------+ +------------+----------+
| mytable_id | | place_id | name | | mytable_id | place_id |
+------------+ +----------+----------+ +------------+----------+
| 1 | | 1 | new york | | 1 | 1 |
| 2 | | 2 | toronto | | 1 | 2 |
| 3 | | 3 | london | | 1 | 3 |
+------------+ +----------+----------+ | 2 | 2 |
| 3 | 1 |
| 3 | 3 |
+------------+----------+
The table mytable_places is what's called a lookup table (or, xref/cross-reference table, or correlation table). Its only job is to keep track of which mytable records have which place records, and vice versa.
From this example we can see that The 1st mytable record has all 3 places, the 2nd has only toronto, and the 3rd has new york and london.
This opens you up too all sorts of queries that would be difficult, expensive, or impossible with your current design.
Want to know how many mytable records have toronto? No problem
SELECT COUNT(*)
FROM mytable_place x
LEFT JOIN place p
ON p.place_id = x.place_id
WHERE p.name = 'toronto';
How about the number of mytable records per place, sorted?
SELECT p.name
, COUNT(*) as `count`
FROM mytable_place x
LEFT JOIN place p
ON p.place_id = x.place_id
GROUP BY p.place_id
ORDER BY `count` DESC, p.name ASC
And these are going to be much faster than any query using LIKE since they can use indexes on columns such as place.name.