I have a table like this:
client_id | Product1 | Product2 | ... | Product170
--------------------------------------------------
4 | Null | 4 | ... | 5
32 | 5 | 3 | ... | Null
22 | 4 | 1 | ... | 3
I want to have the totals for each of my Products. I want a view, or something similar, like this:
product_id | Total
--------------------------------------------------
Product1 | 9
Product2 | 8
...
Preferably leaving out Products that have a sum of 0.
Am I able to do this? I have many columns so I would rather not have a SELECT statement calling each individual column by name.
(Context: This table holds orders for a business. A client will order some products and it is stored here. If you have a better way to organize this info, please let me know).
Am I able to do this?
Yes, by writing an extremely long query with UNION.
Example:
SELECT 'Product1' as product_id, SUM(Product1) as Total
FROM table
UNION
SELECT 'Product2' as product_id, SUM(Product2) as Total
FROM table
UNION
...
Obviously this is not practical, so...
If you have a better way to organize this info, please let me know
A better way to organize this info would be to normalize it using a products table (with a unique id) and a junction table (e.g. client_products). This table contains 3 columns : client_id, product_id and n (the number of product, or whatever your number represents). The primary key is (client_id, product_id), and add an index to product_id.
You can very easily query this model with SELECT product_id, SUM(n) FROM client_products GROUP BY product_id.
You can write the query like
SELECT sum(Product1) as Product1, sum(Product2) as Product1 FROM `product`
It will give you the total of each product but in one row and having product name as column. You can also add the > 0 condition in where clause
Related
I have related products table like this:
product_id | related_product_id
1 | 2
1 | 3
1 | 4
2 | 1
3 | 1
4 | 1
But instead I would like to insert new related product ids so they all match. I.E. If product 1 has 2,3,4 I wan't that products 2,3,4 also have the same related ids which are missing.
Not sure how it's called but is this possible? Many thanks.
You can use a SELECT query as the source of data in an INSERT
INSERT INTO related_products (product_id, related_product_id)
SELECT r1.product_id, r2.related_product_id
FROM related_products AS r1
CROSS JOIN related_products AS r2
WHERE r1.product_id != 1
AND r2.product_id = 1
This join will get all of product 1's related products and combine them with all the other product IDs.
You can give this query a try (untested, make a backup first!):
insert into related_products (product_id,related_product_id) (select related_product_id, product_id from related products);
I would suggest to user bidirectional condition to get the inter related products. For example if you apply condition on single column product_id, you will not get visa-versa result. But, if you check that condition on both column, you will get the result.
For example:
select related_product_id, product_id from products where related_product_id=1 OR product_id=1
This will give your related product id in either related_product_id or product_id.
Same you can get it for product 2 i.e.
select related_product_id, product_id from products where related_product_id=2 OR product_id=2
This will give all your related product id in either related_product_id or product_id.
MySQL 5.6
We have two tables: cars and views.
Cars Views
---+------- ---+-------
id | desc id | car_id
---+------- ---+-------
1 | desc1 1 | 1
2 | Desc1 2 | 2
3 | desc2 3 | 3
The problem is with the desc field in the table cars. That row had to be unique but we unfortunately allowed the users to fill in uppercased values, which brought us to the situation of having (according to the example above) two duplicated rows: desc1 and Desc1.
The way to fix that is DELETE the duplicated cars, and keep only the first one. We know how to deal with that.
Our problem comes before that, when updating the related table, where some views are associated to a car which has a duplicated desc (for instance a car which is going to be removed). Those views should be updated for being assigned to the first of the duplicated cars (in this case the car id #1)
After the UPDATE, we'd like this result in views:
Views
---+-------
id | car_id
1 | 1
2 | 1
3 | 3
We are able to get all the ids of the duplicated cars and deal with the deletion but we're stuck with this UPDATE.
The solution will be to create a mapping table with before/after values for description ids.
The result should look something like:
Before | After
---------------
1 | 1
2 | 1
3 | 3
That table can be created with something like this:
SELECT
cars.id AS before_id,
fixed.lowest_id AS after_id
FROM cars
JOIN (
-- The lowest id value for each duplicate description
SELECT
MIN(id) AS lowest_id,
LOWER(desc) AS lower_desc
FROM cars
GROUP BY LOWER(desc)
) fixed
ON LOWER(cars.desc) = fixed.lower_desc
You can then have your views match to that mapping table to pull the new "correct" id value.
UPDATE Views AS v
JOIN (SELECT c1.id AS oldID, MIN(c2.id) AS newID
FROM Cars AS c1
JOIN Cars AS c2 ON LOWER(c1.desc) = LOWER(c2.desc)
HAVING oldID != newID) AS c
ON v.car_id = oldID
SET v.car_id = newID
The subquery finds the primary ID for each ID that contains a duplicate description. Joining this with the Views table provides the information needed to make the replacements.
For simplicity, I will give a quick example of what i am trying to achieve:
Table 1 - Members
ID | Name
--------------------
1 | John
2 | Mike
3 | Sam
Table 1 - Member_Selections
ID | planID
--------------------
1 | 1
1 | 2
1 | 1
2 | 2
2 | 3
3 | 2
3 | 1
Table 3 - Selection_Details
planID | Cost
--------------------
1 | 5
2 | 10
3 | 12
When i run my query, I want to return the sum of the all member selections grouped by member. The issue I face however (e.g. table 2 data) is that some members may have duplicate information within the system by mistake. While we do our best to filter this data up front, sometimes it slips through the cracks so when I make the necessary calls to the system to pull information, I also want to filter this data.
the results SHOULD show:
Results Table
ID | Name | Total_Cost
-----------------------------
1 | John | 15
2 | Mike | 22
3 | Sam | 15
but instead have John as $20 because he has plan ID #1 inserted twice by mistake.
My query is currently:
SELECT
sq.ID, sq.name, SUM(sq.premium) AS total_cost
FROM
(
SELECT
m.id, m.name, g.premium
FROM members m
INNER JOIN member_selections s USING(ID)
INNER JOIN selection_details g USING(planid)
) sq group by sq.agent
Adding DISTINCT s.planID filters the results incorrectly as it will only show a single PlanID 1 sold (even though members 1 and 3 bought it).
Any help is appreciated.
EDIT
There is also another table I forgot to mention which is the agent table (the agent who sold the plans to members).
the final group by statement groups ALL items sold by the agent ID (which turns the final results into a single row).
Perhaps the simplest solution is to put a unique composite key on the member_selections table:
alter table member_selections add unique key ms_key (ID, planID);
which would prevent any records from being added where the unique combo of ID/planID already exist elsewhere in the table. That'd allow only a single (1,1)
comment followup:
just saw your comment about the 'alter ignore...'. That's work fine, but you'd still be left with the bad duplicates in the table. I'd suggest doing the unique key, then manually cleaning up the table. The query I put in the comments should find all the duplicates for you, which you can then weed out by hand. once the table's clean, there'll be no need for the duplicate-handling version of the query.
Use UNIQUE keys to prevent accidental duplicate entries. This will eliminate the problem at the source, instead of when it starts to show symptoms. It also makes later queries easier, because you can count on having a consistent database.
What about:
SELECT
sq.ID, sq.name, SUM(sq.premium) AS total_cost
FROM
(
SELECT
m.id, m.name, g.premium
FROM members m
INNER JOIN
(select distinct ID, PlanID from member_selections) s
USING(ID)
INNER JOIN selection_details g USING(planid)
) sq group by sq.agent
By the way, is there a reason you don't have a primary key on member_selections that will prevent these duplicates from happening in the first place?
You can add a group by clause into the inner query, which groups by all three columns, basically returning only unique rows. (I also changed 'premium' to 'cost' to match your example tables, and dropped the agent part)
SELECT
sq.ID,
sq.name,
SUM(sq.Cost) AS total_cost
FROM
(
SELECT
m.id,
m.name,
g.Cost
FROM
members m
INNER JOIN member_selections s USING(ID)
INNER JOIN selection_details g USING(planid)
GROUP BY
m.ID,
m.NAME,
g.Cost
) sq
group by
sq.ID,
sq.NAME
Extending further from this question Query to find top rated article in each category -
Consider the same table -
id | category_id | rating
---+-------------+-------
1 | 1 | 10
2 | 1 | 8
3 | 2 | 7
4 | 3 | 5
5 | 3 | 2
6 | 3 | 6
There is a table articles, with fields id, rating (an integer from 1-10), and category_id (an integer representing to which category it belongs). And if I have the same goal to get the top rated articles in each query (this should be the result):-
Desired Result
id | category_id | rating
---+-------------+-------
1 | 1 | 10
3 | 2 | 7
6 | 3 | 6
Extension of original question
But, running the following query -
SELECT id, category_id, max( rating ) AS max_rating
FROM `articles`
GROUP BY category_id
results into the following where everything, except the id field, is as desired. I know how to do this with a subquery - as answered in the same question - Using subquery.
id category_id max_rating
1 1 10
3 2 7
4 3 6
In generic terms
Excluding the grouped column (category_id) and the evaluated columns (columns returning results of aggregate function like SUM(), MAX() etc. - in this case max_rating), the values returned in the other fields are simply the first row under every grouped result set (grouped by category_id in this case). E.g. the record with id =1 is the first one in the table under category_id 1 (id 1 and 2 under category_id 1) so it is returned.
I am just wondering is it not possible to somehow overcome this default behavior to return rows based on conditions? If mysql can perform calculation for every grouped result set (does MAX() counting etc) then why can't it return the row corresponding to the maximum rating. Is it not possible to do this in a single query without a subquery? This looks to me like a frequent requirement.
Update
I could not figure out what I want from Naktibalda's solution too. And just to mention again, I know how to do this using a subquery, as again answered by OMG Ponies.
Use:
SELECT x.id,
x.category_id,
x.rating
FROM YOUR_TABLE x
JOIN (SELECT t.category_id,
MAX(t.rating) AS max_rating
FROM YOUR_TABLE t
GROUP BY t.category_id) y ON y.category_id = x.category_id
AND y.max_rating = x.rating
Is it possible to make multiple queries at once within the same query?
Here is an example of what I'm trying to do.
We have the following table:
| userid | price | stock | description |
----------------------------------------
1 10.00 5 some text
2 25.00 2 some text
3 15.00 3 some text
4 35.00 2 some text
5 30.00 4 some text
The queries that I'm trying to do are:
the MIN and MAX price group by description
the price set by userid 2
Stock and price of the first three results only without grouping
So the HTML table will look like this:
description | Min_Price | Max_Price | Price Set by userid 2 | 1st Price | 1st Stock | 2nd Price | 2nd Stock | 3rd Price | 3rd Stock
This solution will require a number of "sub queries" or joins.
You are probably looking at something like:
select t1.description,
t1.min_price,
t1.max_price,
t2.user_id_2_price
from
(select description, min(price) as min_price, max(price) as max_price from t group by description) t1
left join
(select price, description as user_id_2_price from t where userid = '2') t2
on
(t1.description = t2.description)
And you can add as many of these "left joins" as you need
MySQL Unions (http://dev.mysql.com/doc/refman/5.0/en/union.html) should probably put you on the right track.
You could also do most of it with sub selects, though that is probably not a great idea.
Use union or, to have everything in one row, something like that:
Select
(Select min(price) from table) as min_price,
(Select max(price) from table) as max_price,
....