MYSQL Query for SUM and DISTINCT? - mysql

I have a database with the first five columns like this:
ID NAME QUANTITY PRICE KIND
1 Dog 2 5 A
2 Cat 1 6 B
3 Dog 2 5 C
4 Bird 5 5 C
(DOG QUANTITY and PRICE will always be the same)
What I want to do to is to something like
SELECT KIND, SUM(QUANTITY * PRICE) GROUP BY KIND WHERE DISTINCT NAME
So that I get something that looks like this:
A 10
B 6
C 25
(The duplicate DOG is eliminated.)
I know my syntax above is grossly wrong -- it's just seems to be the most eloquent way of explaining what sort of thing I'm looking for.
In other words, I want to get rid of non-distinct NAMES then SUM the rest. I seem to be able to do one or the other but not both.
Any ideas? If worse comes to worst I can do it as a loop in PHP rather than as a single MYSQL query.

I'm not really clear about either what the rules are or why your table is in that format (with repeated name, quantity,price) but here is one way of getting your expected output.
select kind, SUM(quantity*price)
from
(
SELECT name, quantity, price, min(kind) kind
FROM YourTable
group by name, quantity, price
) t
group by kind

Here I chose the item with the lowest ID as the one to keep:
Select T.Kind, Sum( T.Quantity * T.Price ) As Total
From Table As T
Where Id = (
Select Min(T2.Id)
From Table As T2
Where T2.Name = T.Name
)
Group By T.Kind
Assuming that your table is unique on Name and Kind, you can do:
Select T.Kind, Sum( T.Quantity * T.Price ) As Total
From Table As T
Where T.Kind = (
Select Min(T2.Kind)
From Table As T2
Where T2.Name = T.Name
)
Group By T.Kind

Related

How do optimise sql query using join between multiple tables

i have two tables having following structure
Table A
itemId categoryId orderDate
==========================================
1 23 2016-11-08
1 23 2016-11-12
1 23 2016-11-16
Table B have the structure
categoryId stock price
==========================================
23 500 600
However mine desired output should be as like
Result C
price stock orderdate qty
600 500 2016-11-08 (first order date) 3 (3 time appearance in first table)
Here is what i have tried so far
select b.price,b.stock from B b, A a
where b.categoryId = (
select a.categoryId
from A
GROUP BY categoryId
HAVING COUNT(categoryId)>1
)
and (a.orderdate = (
select MIN(orderdate)
from A
where categoryId = b.categoryId)
)
i have following result
price stock orderdate
600 500 2016-11-08
i have no idea how do find qty as it is appeared 3 times in first table.
I think you want the records in table a grouped by item id and category id, so include these two in your group by statement. Then the other columns you have to aggregate using MIN, MAX, AVG, SUM, etc. I use MIN which will give you the smallest number in the group for that particular column, although it shouldn't matter in this case whether you use MIN or MAX or AVG - it's all the same. Then COUNT(*) will just count the number of recrods in the group.
Also, joins are generally preferred over listing tables with commas.
SELECT a.itemid, a.categoryid, MIN(b.price), MIN(b.stock), min(a.orderdate), count(*) as qty
FROM a
INNER JOIN b ON a.categoryid = b.categoryid
GROUP BY a.itemid, a.categoryid
You also need to select COUNT(*)
how about use following sql
select min(price), min(stock), min(orderDate), COUNT(categoryId)
from A,B where A.categoryId = B.categoryId
GROUP by A.categoryId
You could create views for your subqueries and give them meaningful names e.g. CategoriesUsedInMultipleOrders, MostRecentOrderByCategory. This would 'optimize' you query by abstracting away complexity and making it easier for the human reader to understand.
This is the Query with the appropriate join method see Result:
SELECT B.price, B.stock, MIN( A.orderDate ) AS orderdate, COUNT( * ) AS qty
FROM TableA A, TableB B
WHERE A.categoryId = B.CategoryId
GROUP BY A.categoryId, B.price, B.stock

MySQL Conditional count based on a value in another column

I have table that looks like this:
id rank
a 2
a 1
b 4
b 3
c 7
d 1
d 1
e 9
I need to get all the distinct rank values on one column and count of all the unique id's that have reached equal or higher rank than in the first column.
So the result I need would be something like this:
rank count
1 5
2 4
3 3
4 3
7 2
9 1
I've been able to make a table with all the unique id's with their max rank:
SELECT
MAX(rank) AS 'TopRank',
id
FROM myTable
GROUP BY id
I'm also able to get all the distinct rank values and count how many id's have reached exactly that rank:
SELECT
DISTINCT TopRank AS 'rank',
COUNT(id) AS 'count of id'
FROM
(SELECT
MAX(rank) AS 'TopRank',
id
FROM myTable
GROUP BY id) tableDerp
GROUP BY TopRank
ORDER BY TopRank ASC
But I don't know how to get count of id's where the rank is equal OR HIGHER than the rank in column 1. Trying SUM(CASE WHEN TopRank > TopRank THEN 1 END) naturally gives me nothing. So how can I get the count of id's where the TopRank is higher or equal to each distinct rank value? Or am I looking in the wrong way and should try something like running totals instead? I tried to look for similar questions but I think I'm completely on a wrong trail here since I couldn't find any and this seems a pretty simple problem that I'm just overthinking somehow. Any help much appreciated.
One approach is to use a correlated subquery. Just get the list of ranks and then use a correlated subquery to get the count you are looking for:
SELECT r.rank,
(SELECT COUNT(DISTINCT t2.id)
FROM myTable t2
WHERE t2.rank >= r.rank
) as cnt
FROM (SELECT DISTINCT rank FROM myTable) r;

SQL statement to get similar records inside lookup table

I have a table in mysql of hashtags:
strHash | nPersonID
-------------------------
#dogowner | 1
#catowner | 1
#dogowner | 2
#mouseowner | 3
#fish | 3
#vancouver | 1
#vancouver | 3
I'd like to query the table with a MySQL statement and say, I want nPersonID = 1, return a result of all people that share one ore more strHash values.
This will allow me to show similar people when viewing a specific person. (or, people who share similar hash tags - some but not necessarily all)
The simplest way to get just a list of people that share hastags with person 1 is this:
SELECT DISTINCT nPersonID
FROM table
WHERE strHash IN (
SELECT b.strHash
FROM table b
WHERE b.nPersonID=1
)
But you can get some little bit of extra info without making things too complex.
For example, this query will list the same people, but also give you a quantifier of how similar these people's hashtags are to those person 1:
SELECT nPersonID, COUNT(DISTINCT strHash) AS shared_hashtags
FROM table
WHERE strHash IN (
SELECT b.strHash
FROM table b
WHERE b.nPersonID=1
)
GROUP BY nPersonID
ORDER BY shared_hashtags DESC
This will select all the people that have one or more of the hashtags that person 1 has... including person 1.
The resuling list will be ordered by the number of common hastags... with the most similar people on top (including person 1).
Try this SQL:
SELECT DISTINCT t2.nPersonID
FROM table t1
JOIN table t2
ON t1.strHash = t2.strHash
WHERE t1.nPersonID = 1
The idea:
Get the hashtags of the selected person
Get all persons which have similar hashtags
Count how many hastags are matching
The query:
SELECT
S.nPersonId
, COUNT(1) AS SimilarHashCount
FROM (
SELECT
strHash
FROM
hashtags
WHERE
nNersonID = 1
) P
INNER JOIN (
SELECT
strHash
, nPersonD
FROM
hashtags
WHERE
nNersonID != 1
) S
ON P.strHash = S.strHash
GROUP BY
S.nPersonID
ORDER BY
COUNT(1) DESC
You can limit the result, or you can use the HAVING statement to filter the results
Many ways to achieve this. I'll do it with following query
SELECT DISTINCT [nPersonID]
FROM [htag]
where strHash in (select strHash FROM [htag] where [nPersonID] = 1)
This will output as below
nPersonID
1
2
3
If you wish to get result without the ID '1' (request id) add another condition
SELECT DISTINCT [nPersonID]
FROM [htag]
where strHash in (select strHash FROM [htag] where [nPersonID] = 1)
and [nPersonID] != 1
Output:
nPersonID
2
3
Hope this helps!
SELECT *, COUNT(*) FROM XXX GROUP BY strHash HAVING COUNT(*) > 1
This question has been asked before - How to select non "unique" rows
The answer above belongs to judda (a bit modified)

Elegant mysql to select, group, combine multiple rows from one table

Here is a simplified version of my table:
group price spec
a 1 .
a 2 ..
b 1 ...
b 2
c .
. .
. .
I'd like to produce a result like this: (I'll refer to this as result_table)
price_a |spec_a |price_b |spec_b |price_c ...|total_cost
1 |. |1 |.. |... |
(min) (min) =1+1+...
Basically I want to:
select the rows containing the min price within each group
combine columns into a single row
I know this can be done using several queries and/or combined with some non-sql processing on the results, but I suspect that there maybe better solutions.
The reason that I want to do task 2 (combine columns into a single row)
is because I want to do something like the following with the result_table:
select *,
(result_table.total_cost + table1.price + table.2.price) as total_combined_cost
from result_table
right join table1
right join table2
This may be too much to ask for, so here is some other thoughts on the problem:
Instead of trying to combine multiple rows(task 2), store them in a temporary table
(which would be easier to calculate the total_cost using sum)
Feel free to drop any thoughts, don't have to be complete answer, I feel it's brilliant enough if you have an elegant way to do task 1 !
==Edited/Added 6 Feb 2012==
The goal of my program is to identify best combinations of items with minimal cost (and preferably possess higher utilitarian value at the same time).
Consider #ypercube's comment about large number of groups, temporary table seems to be the only feasible solution. And it is also pointed out there is no pivoting function in MySQL (although it can be implemented, it's not necessary to perform such operation).
Okay, after study #Johan's answer, I'm thinking about something like this for task 1:
select * from
(
select * from
result_table
order by price asc
) as ordered_table
group by group
;
Although looks dodgy, it seems to work.
==Edited/Added 7 Feb 2012==
Since there could be more than one combination may produce the same min value, I have modified my answer :
select result_table.* from
(
select * from
(
select * from
result_table
order by price asc
) as ordered_table
group by group
) as single_min_table
inner join result_table
on result_table.group = single_min_table.group
and result_table.price = single_min_table.price
;
However, I have just realised that there is another problem I need to deal with:
I can not ignore all the spec, since there is a provider property, items from different providers may or may not be able to be assembled together, so to be safe (and to simplify my problem) I decide to combine items from the same provider only, so the problem becomes:
For example if I have an initial table like this(with only 2 groups and 2 providers):
id group price spec provider
1 a 1 . x
2 a 2 .. y
3 a 3 ... y
4 b 1 ... y
5 b 2 x
6 b 3 z
I need to combine
id group price spec provider
1 a 1 . x
5 b 2 x
and
2 a 2 .. y
4 b 1 ... y
record (id 6) can be eliminated from the choices since it dose not have all the groups available.
So it's not necessarily to select only the min of each group, rather it's to select one from each group so that for each provider I have a minimal combined cost.
You cannot pivot in MySQL, but you can group results together.
The GROUP_CONCAT function will give you a result like this:
column A column B column c column d
groups specs prices sum(price)
a,b,c some,list,xyz 1,5,7 13
Here's a sample query:
(The query assumes you have a primary (or unique) key called id defined on the target table).
SELECT
GROUP_CONCAT(a.`group`) as groups
,GROUP_CONCAT(a.spec) as specs
,GROUP_CONCAT(a.min_price) as prices
,SUM(a.min_prices) as total_of_min_prices
FROM
( SELECT price, spec, `group` FROM table1
WHERE id IN
(SELECT MIN(id) as id FROM table1 GROUP BY `group` HAVING price = MIN(price))
) AS a
See: http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html
Producing the total_cost only:
SELECT SUM(min_price) AS total_cost
FROM
( SELECT MIN(price) AS min_price
FROM TableX
GROUP BY `group`
) AS grp
If a result set with the minimum prices returned in row (not in column) per group is fine, then your problem is of the gretaest-n-per-group type. There are various methods to solve it. Here's one:
SELECT tg.grp
tm.price AS min_price
tm.spec
FROM
( SELECT DISTINCT `group` AS grp
FROM TableX
) AS tg
JOIN
TableX AS tm
ON
tm.PK = --- the Primary Key of the table
( SELECT tmin.PK
FROM TableX AS tmin
WHERE tmin.`group` = tg.grp
ORDER BY tmin.price ASC
LIMIT 1
)

mysql query to find sum of different names

I use similar queries (10) as following queries (modified) to find sum
SELECT sum(amount) AS amount
FROM `students`
WHERE sex='M'
&& name in ('salil', 'anil', 'gaikwad')
...and:
SELECT sum(amount) AS amount
FROM `students`
WHERE sex='M'
&& name in ('salil1', 'anil1', 'gaikwad1')
i want to make a single query of the above 10 queries. is it possible?
You can use UNION
SELECT 'subset1', sum(amount) AS amount FROM students WHERE sex='M' and name in ('salil', 'anil', 'gaikwad')
UNION
SELECT 'subset2', sum(amount) AS amount FROM students WHERE sex='M' and name in ('salil1', 'anil1', 'gaikwad1')
However, you probably query these sets of students for a reason, perhaps anil, salil and gaikwad are one group of students. If so, you should reflect this in the database structure, not in your code.
You could add a field 'SUbset' or 'Group' or whatever that is, to students table, so it looks like this:
name group_id
salil 1
anil 1
gaikwad 1
salil1 2
...
Then you can do
select group_id, sum(amount) from students group by group_id
Try something like this
SELECT sum(amount) AS amount
FROM students INNER JOIN
(SELECT 'salil%' Val UNION SELECT 'anil%' UNION SELECT 'gaikwad%') s ON students.NAME LIKE s.Val
WHERE sex='M'
This allows you to use the values in the second Table to join with LIKE.