MySQL - COUNT scatter classified by multiple records - mysql

I have SQL table:
CREATE TABLE `test_results` (
`id` int AUTO_INCREMENT,
`date_time` datetime,
`altpn` varchar(60),
`vsp` decimal(10,4),
PRIMARY KEY (`id`)
);
VALUES
(null, 2016-07-22 13:30:01, pn1, 14.55),
(null, 2016-07-22 13:30:02, pn1, 14.45),
(null, 2016-07-22 13:30:03, pn1, 14.55),
(null, 2016-07-22 13:30:04, pn2, 14.45),
(null, 2016-07-22 13:30:05, pn2, 14.65),
(null, 2016-07-22 13:30:06, pn2, 14.45);
And i need result like this:
vsp - altpn - COUNT
14.45 - pn1 - 1
14.45 - pn2 - 2
14.55 - pn1 - 2
14.55 - pn2 - 0
14.65 - pn1 - 0
14.65 - pn2 - 1
It can also be altpn - vsp - COUNT, it does not matter. But every vsp value must contain all pns, which are in table. Even that with zero value.
Is this even possible to do? I can do normal SQL like:
SELECT test_results.vsp,
test_results.altpn,
COUNT(*)
FROM test_results
GROUP BY test_results.vsp,
test_results.altpn
and re-compute this result in php to table what i need (For chart), but it will by easier do it in SQL.

SELECT d.*,
c.*,
(SELECT count(*)
FROM test_results e
WHERE e.altpn=d.idaltpn
AND e.vsp=c.idvsp)
FROM
(SELECT distinct(a.altpn) idaltpn
FROM test_results a) d,
(SELECT distinct(a.vsp) idvsp
FROM test_results a) c

You are close. You'll want to assemble together a list of all the unique vsp/altpn and then left join that to a count subquery. I'm going to use a crossjoin of distinct values of vps to the distinct values of altpn to create that list for you...you might want to use business logic if there is something that determines when a vsp/altpn combination is valid in your results.
select a.vsp, a.altpn, ifnull(counts.totalcount,0)
from (select vsp.vsp, altpn.altpn
from (select vsp
from test_results
group by vsp)vsp
join
(select altpn
from test_results
group by altpn)altpn
on 1=1) a
left join
(SELECT test_results.vsp, test_results.altpn, COUNT(*) as totalcount
FROM test_results
GROUP BY test_results.vsp, test_results.altpn) counts
on a.vsp = counts.vsp and a.altpn = counts.altpn

Related

Mysql: How to join a query to find results from another table

I have two tables:
TABLE A
Unique_id
id
price
1
1
10.50
2
3
14.70
3
1
12.44
TABLE B
Unique_id
Date
Category
Store
Cost
1
2022/03/12
Shoes
A
13.24
2
2022/04/15
Hats
A
15.24
3
2021/11/03
Shoes
B
22.31
4
2000/12/14
Shoes
A
15.33
I need to filter TABLE A on a known id to get the Unique_id and average price to join to Table B.
Using this information I need to know which stores this item was sold in.
I then need to create a results table displaying the stores and the amount of days sales were recorded in the stores - regardless of whether the sales are associated with the id and the average cost.
To put it more simply I can break down the task into 2 separate commands:
SELECT AVG(price)
FROM table_a
WHERE id = 1
GROUP BY unique_id;
SELECT store, COUNT(date), AVG(cost)
FROM table_b
WHERE category = 'Shoes'
GROUP BY store;
The unique_id should inform the join but when I join the tables it messes up my COUNT function and only counts the days in which the id is connected - not the total store sales days.
The results should look something like this:
Store
AVG price
COUNT days
AVG cost
A
10.50.
3
14.60.
B
12.44
1.
22.31.
I wwas hard to grasp, what you wanted, but after some thinking and your clarification, it can be solved as the code shows
CREATE TABLE TableA
(`Unique_id` int, `id` int, `price` DECIMAL(10,2))
;
INSERT INTO TableA
(`Unique_id`, `id`, `price`)
VALUES
(1, 1, 10.50),
(2, 3, 14.70),
(3, 1, 12.44)
;
CREATE TABLE TableB
(`Unique_id` int, `Date` datetime, `Category` varchar(5), `Store` varchar(1), `Cost` DECIMAL(10,2))
;
INSERT INTO TableB
(`Unique_id`, `Date`, `Category`, `Store`, `Cost`)
VALUES
(1, '2022-03-12 01:00:00', 'Shoes', 'A', 13.24),
(2, '2022-04-15 02:00:00', 'Hats', 'A', 15.24),
(3, '2021-11-03 01:00:00', 'Shoes', 'B', 22.31),
(4, '2000-12-14 01:00:00', 'Shoes', 'A', 15.33)
SELECT
B.`Store`
, AVG(A.`price`) price
, (SELECT COUNT(*) FROM TableB WHERE `Store` = B.`Store` ) count_
, (SELECT AVG(
`cost`) FROM TableB WHERE `Store` = B.`Store` ) price
FROM TableA A
JOIN TableB B ON A.`Unique_id` = B.`Unique_id`
WHERE B.`Category` = 'Shoes'
GROUP BY B.`Store`
Store | price | count_ | price
:---- | --------: | -----: | --------:
A | 10.500000 | 3 | 14.603333
B | 12.440000 | 1 | 22.310000
db<>fiddle here
This should be the query you are after. Mainly you simply join the rows using an outer join, because not every table_b row has a match in table_a.
Then, the only hindrance is that you only want to consider shoes in your average price. For this to happen you use conditional aggregation (a CASE expression inside the aggregation function).
select
b.store,
avg(case when b.category = 'Shoes' then a.price end) as avg_shoe_price,
count(b.unique_id) as count_b_rows,
avg(b.cost) as avg_cost
from table_b b
left outer join table_a a on a.unique_id = b.unique_id
group by b.store
order by b.store;
I must admit, it took me ages to understand what you want and where these numbers result from. The main reason for this is that you have WHERE table_a.id = 1 in your query, but this must not be applied to get the result you are showing. Next time please look to it that your description, queries and sample data match.
(And then, I think that names like table_a, table_b and unique_id don't help understanding this. If table_a were called prices instead and table_b costs and unique_id were called cost_id then, I wouldn't have had to wonder how the tables are related (by id? by unique id?) and wouldn't have had to look again and again which table the cost resides in, which table has a price and which table is the outer joined one while looking at the problem, the requested result and while writing my query.)

Need list of data using DISTINCT, COUNT, MAX

The table structure is as below,
My first SQL query is as below,
SELECT DISTINCT(IndustryVertical)
, COUNT(IndustryVertical) AS IndustryVerticalCount
, City
FROM `records`
WHERE City!=''
GROUP
BY IndustryVertical
, City
ORDER
BY `IndustryVerticalCount` DESC
by running the above query I'm getting the below,
What I'm trying to achieve is to get the List of all the DISTINCT CITY with ONLY ONE MAX(IndustryVerticalCount) and IndustryVertical.
Tried several things with no hope.
Anyone, please guide me.
There're several records in each City values. what I'm trying to achieve is that getting,
All the distinct City Values
The MAX COUNT of industryVertical
Name of industryVertical
The record I'm getting is as below,
What I'm trying to get,
The above record is reference purpose. Here, you can see only distinct city values with only one the vertical name having max count.
Since you are using group by, it will automatically select only distinct rows. Since you are using group by on two columns, you will get rows in which only combination of both columns is distinct.
What you now have to do is use this resulting table, and perform a query on it to find the maximum count grouped by city.
SELECT IndustryVertical, IndustryVerticalCount, City from
( SELECT IndustryVertical
, COUNT(IndustryVertical) AS IndustryVerticalCount
, City
FROM `records`
WHERE City!=''
GROUP
BY IndustryVertical
, City) as tbl where IndustryVerticalCount IN (Select max(IndustryVerticalCount) from ( SELECT IndustryVertical
, COUNT(IndustryVertical) AS IndustryVerticalCount
, City
FROM `records`
WHERE City!=''
GROUP
BY IndustryVertical
, City) as tbl2 where tbl.City=tbl2.city)
This may not be the most efficient method, but I think it will work.
How about this? I think it should be worked:
DECLARE #DataSet TABLE (
City VARCHAR(50),
IndustryVertical VARCHAR(50),
IndustryVerticalCount INT
)
INSERT INTO #DataSet SELECT 'Bangalore', 'Consumer Internet', 279
INSERT INTO #DataSet SELECT 'Bangalore', 'Technology', 269
INSERT INTO #DataSet SELECT 'Bangalore', 'Logistics', 179
INSERT INTO #DataSet SELECT 'Mumbai', 'Technology', 194
INSERT INTO #DataSet SELECT 'Mumbai', 'Consumer Internet', 89
SELECT
table_a.*
FROM #DataSet table_a
LEFT JOIN #DataSet table_b
ON table_a.City = table_b.City
AND table_a.IndustryVerticalCount < table_b.IndustryVerticalCount
WHERE table_b.IndustryVerticalCount IS NULL
I think you simply want a HAVING clause:
SELECT r.IndustryVertical,
COUNT(*) AS IndustryVerticalCount,
r.City
FROM records r
WHERE r.City <> ''
GROUP BY r.IndustryVertical, r.City
HAVING COUNT(*) = (SELECT COUNT(*)
FROM records r2
WHERE r2.City = r.City
ORDER BY COUNT(*) DESC
LIMIT 1
)
ORDER BY IndustryVerticalCount DESC;

Force MySQL to return a result for all option within IN

I have a simple MySQL statement:
SELECT q1, COUNT(q1) FROM results WHERE q1 IN ('1','2','3');
Currently there are only results for 1 and 3 - results are:
1 = 6
3 = 7
But what I need is for MySQL to bring back a result for 1,2 and 3 even though 2 has no data, as this:
1 = 6
2 = 0
3 = 7
Any ideas?
This is tricky because no rows match your value (2), they cannot be counted.
I would solve this by creating a temp table containing the list of values I want counts for:
CREATE TEMPORARY TABLE q ( q1 INT PRIMARY KEY );
INSERT INTO q (q1) VALUES (1), (2), (3);
Then do an OUTER JOIN to your results table:
SELECT q.q1, COALESCE(COUNT(*), 0) AS count
FROM q LEFT OUTER JOIN results USING (q1)
GROUP BY q.q1;
This way each value will be part of the final result set, even if it has no matching rows.
Re comment from #Mike Christensen:
MySQL doesn't support CTE's, in spite of it being requested as far back as 2006: http://bugs.mysql.com/bug.php?id=16244
You could do the same thing with a derived table:
SELECT q.q1, COALESCE(COUNT(*), 0) AS count
FROM (SELECT 1 AS q1 UNION ALL SELECT 2 UNION ALL SELECT 3) AS q
LEFT OUTER JOIN results USING (q1)
GROUP BY q.q1;
But this creates a temp table anyway.
A SQL query doesn't really have a way to refer to the values in your IN clause. I think you'd have to break this down into one query for each value. Something like:
SELECT 1 as q1, COUNT(1) FROM results WHERE q1 = '1'
UNION ALL
SELECT 2 as q1, COUNT(1) FROM results WHERE q1 = '2'
UNION ALL
SELECT 3 as q1, COUNT(1) FROM results WHERE q1 = '3'
Fiddle
Note: If there are a lot of values in your IN clause, you might be better off to write your code in a way where missing values are assumed to have zero.
In general, you cannot query something that does not exists. So, you must create data for it. Use union to add those missing data values.
select q1, COUNT(*)
from results
where q1 in ('1','2','3')
group by q1
union
select q1, 0
from (
select '1' as q1
union
select '2'
union
select '3'
) as q
where q1 not in (
select q1
from results
)

mysql: grouping fields in one table separately

I need to get the averages for the following kind of data out of MySQL:
There is one table:
table 1
field_a, field_b, field_c
data_a , data_a , data_c
data_a , data_b , data_c
data_a , data_a , data_c
what I need to output is (this part does not need to be in mysql, just explaining the data I need) :
100% of field_a is data_a
33% of field_b is data_b and 66% is data_a
100% of field_c is data_c
I tried grouping by each field but it did not give the desired results. What would be the best way to approach this? (currently I've just set it up to run separate queries for each field but there are quite a few so I would like them in one big query/something a little more efficient).
also, apologies for the title. I couldn't really think of a better way to explain it. Feel free to edit with something more succinct.
I can only think of UNION use:
(SELECT field_a AS val, COUNT(field_a) AS cnt, 'A' AS fld_name
FROM table1 GROUP BY field_a)
UNION
(SELECT field_b AS val, COUNT(field_b) AS cnt, 'B' AS fld_name
FROM table1 GROUP BY field_b)
UNION
(SELECT field_c AS val, COUNT(field_c) AS cnt, 'C' AS fld_name
FROM table1 GROUP BY field_c)
It will give us the record set containing in each row:
Field value,
Number of such values in table,
Field name.
select 'field a', fa, cfa * 100.0 / total
from (
select
fa, count(fa) as cfa
from t
group by fa
) t
inner join (
select count(*) as total
from t
) s on 1 = 1
union
select 'field b', fb, cfb * 100.0 / total
from (
select
fb, count(fb) as cfb
from t
group by fb
) t
inner join (
select count(*) as total
from t
) s on 1 = 1
union
...
If you are looking for a dynamically built query it can be done but this is not it.

MySQL sum() on different group bys

Ok, I have a query over two tables. I need to get two sums. I do a group by so the sum() works correctly.
SELECT sum(a.x), sum(b.y) FROM a,b GROUP BY a.n where a.n=b.m
So far this works well, but the problem is i need to group them differently for the second sum (sum(b.y)), than for the first sum (sum(a.x)).
The real query is somewhat more complex but this is my main problem.
This is what i actually try to select sum(stock.amount) - if( sold.amount IS NULL , 0, sum( sold.amount ) )
How can I solve that in one query?
since you are not writing down the tables I am gonna make a wild guess and assume the tables are like :
stock : id, item_id, amount
sold : id, item_id, amount
then again I assume that you need the stock_in_total, sold_total, left_total counts
SELECT
stock_sums.item_id,
stock_sums.st_sum as stock_in_total,
COALESCE(sold_sums.so_sum,0) as sold_total,
(stock_sums.st_sum - COALESCE(sold_sums.so_sum,0)) as left_total
FROM (
SELECT stock.item_id as item_id, SUM(stock.amount) as st_sum
FROM stock
GROUP BY item_id
) as stock_sums
LEFT JOIN (
SELECT sold.item_id as item_id, SUM(sold.amount) as so_sum
FROM sold
GROUP by item_id
) as sold_sums ON stock_sums.item_id = sold_sums.item_id
I hope this would help.
Here is how I would do it. I assume that Stock is the main table, with an ID and an amount, and that Sold maps to Stock via an ID value, and has zero to many records for each Stock item.
SELECT Q1.id, Q1.Total1, Q2.Total2
, Q1.Total1 - COALESCE(Q2.Total2,0) as Outstanding
FROM (
SELECT id, SUM(amount) as Total1
FROM Stock GROUP BY id
) as Q1
LEFT OUTER JOIN (
SELECT id, SUM(Amount) as Total2
FROM Sold GROUP BY id
) as Q2
ON Q2.id = Q1.id
Note that simply formatting your SQL into a clean way forces you to break it into logical parts and will often reveal exactly what is wrong with the query.
The example above also handles correctly the cases where there is not match in the Sold table.
Cheers,
Daniel
(Code Assumptions)
DROP TABLE Stock
CREATE TABLE Stock (
id integer
, amount decimal(10,2)
)
INSERT INTO Stock (id, amount ) VALUES ( 1, 10.1);
INSERT INTO Stock (id, amount ) VALUES ( 2, 20.2);
INSERT INTO Stock (id, amount ) VALUES ( 3, 30.3);
SELECT * FROM STOCK
DROP TABLE Sold
CREATE TABLE Sold (
id integer
, amount decimal(10,2)
)
INSERT INTO Sold (id, amount ) VALUES ( 1, 1.1);
INSERT INTO Sold (id, amount ) VALUES ( 1, 2.2);
INSERT INTO Sold (id, amount ) VALUES ( 1, 3.3);
INSERT INTO Sold (id, amount ) VALUES ( 2, 2.22);
SELECT * FROM Sold
SELECT Q1.id, Q1.Total1, Q2.Total2
, Q1.Total1 - COALESCE(Q2.Total2,0) as Outstanding
FROM (
SELECT id, SUM(amount) as Total1
FROM Stock GROUP BY id
) as Q1
LEFT OUTER JOIN (
SELECT id, SUM(Amount) as Total2
FROM Sold GROUP BY id
) as Q2
ON Q2.id = Q1.id
Results:
id Total1 Total2 Outstanding
1 10.10 6.60 3.50
2 20.20 2.22 17.98
3 30.30 30.30
REVISION
It sounds like you want the total amount of stock you have as one count for each different stock. Then you want how much stock you have left for each stock based on what has been sold. Correct?
If so check this out:
select stock, sum(a.x) as sharesBeforeSale, (sum(a.x) - sum(b.y)) as sharesAfterSale
FROM db.table1 a, db.table2 b
WHERE a.UNIQUEID = b.UNIQUEID AND b.y IS NOT NULL
GROUP BY a.UNIQUEID;
Does that accomplish what you are looking to do?
stock sharesBeforeSale sharesAfterSale
duk 100 25
orc 101 101
yrc 54 41
Enjoy!
Sample tables
db.table1 (stock owned):
UNIQUEID x stock
1 100 duk
2 101 orc
3 54 yrc
db.table2 (stock sold):
UNIQUEID y
1 75
2 0
3 13