MySQL Count and sum based on condition - mysql

Is there a way to count SN for each PN and sum it based on a condition (in below case Loc)?
create table table1 (
code int(10) primary key,
PN varchar(10) not null,
SN varchar(10) not null,
Loc varchar(10));
insert into table1 values (1,'T1','a1','a');
insert into table1 values (2,'T1','a2','a');
insert into table1 values (3,'T1','a3','a');
insert into table1 values (4,'T1','a4','b');
insert into table1 values (5,'T1','a5','b');
insert into table1 values (6,'T1','a6','b');
insert into table1 values (7,'T2','a1','a');
insert into table1 values (8,'T2','a2','a');
insert into table1 values (9,'T2','a3','a');
insert into table1 values (10,'T2','a4','b');
insert into table1 values (11,'T2','a5','b');
insert into table1 values (12,'T2','a6','b');
insert into table1 values (13,'T2','a7','b');
The results I try to achieve is:
PN a b
T1 3 3
T2 3 4

This is just conditional aggregation:
select pn, sum(loc = 'a') as a, sum(loc = 'b') as b
from table1
group by pn;
If you have an unknown list of loc values, then you might need a dynamic query. Google "MySQL dynamic pivot".

You can use conditional aggregation :
select PN, sum(case when Loc = 'a' then 1 else 0 end) as a,
sum(case when Loc = 'b' then 1 else 0 end) as b
from table1 t1
group by PN;

You can Try this one
select PN, count(case when Loc = 'a' then 1 else null end) a, count(case when Loc = 'b' then 1 else null end) b
from table1
group by PN

Related

Select sum with input from grouped query

I need to make a query that selects a grouped collection of rows from a table based on user input conditions, and then in the select i will sum data from a subset of the rows.
The setup is rather expansive to describe in a post, so here is a demostration of the problem in the simplest way i can make it:
We have this table: DemoTable
ID
StaticKey
GroupKey
Value
1
A
A
2
2
A
A
2
3
A
B
2
4
A
B
2
5
A
C
2
6
A
C
2
I make a select and groups on "StaticKey".
What i would then like to do, is to, in the select clause, to select the sum of a subset of the values from the groupped data:
select
DT.GroupKey,
(select sum(D.Value) from DemoTable D where D.ID in (DT.ID) and D.GroupKey = 'A') as 'Sum of A''s',
(select COUNT(D.ID) from DemoTable D where D.ID in (DT.ID) and D.GroupKey = 'A') as 'Count of A''s'
from DemoTable DT
group by DT.StaticKey;
I hoped that the sum would result in a sum of 4 and a count of 2, but i get 2 and 1. So the input to the "select sum" seems to be just one id and not the collected ids.
GroupKey
Sum of A's
Count of A's
A
2
1
If i add a group_concat of DT.ID i get them comma separated - but is it posible to get them as a collection i can use as input to the selects?
Heres sql to create the table and queries:
CREATE TABLE DemoTable
(
ID INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
GroupKey varchar(200) null default null,
StaticKey varchar(200) not null default 'A',
Value varchar(200) null default null,
PRIMARY KEY (ID)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8;
insert into DemoTable (GroupKey, Value) values ('A', 2);
insert into DemoTable (GroupKey, Value) values ('A', 2);
insert into DemoTable (GroupKey, Value) values ('B', 2);
insert into DemoTable (GroupKey, Value) values ('B', 2);
insert into DemoTable (GroupKey, Value) values ('C', 2);
insert into DemoTable (GroupKey, Value) values ('C', 2);
select DT.GroupKey,
(select sum(D.Value) from DemoTable D where D.ID in (DT.ID) and D.GroupKey = 'A') as 'Sum of A''s',
(select COUNT(D.ID) from DemoTable D where D.ID in (DT.ID) and D.GroupKey = 'A') as 'Count of A''s'
from DemoTable DT
group by DT.StaticKey;
DROP TABLE DemoTable;
More simple:
select GroupKey,
sum(Value) as sum_of_A,
sum(GroupKey='A') as count_of_A
from DemoTable
where GroupKey='A'
group by GroupKey;
https://dbfiddle.uk/sdYlTw57
Isn't it always D.ID in (DT.ID)?
select
DT.GroupKey,
(select sum(D.Value) from DemoTable D where D.GroupKey = 'A') as 'Sum of A''s',
(select COUNT(D.ID) from DemoTable D where D.GroupKey = 'A') as 'Count of A''s'
from DemoTable DT
group by DT.StaticKey;
It does the job but perhaps it's too simple...
You can also try this.
SELECT DT.GroupKey,
SUM(DT.Value) AS 'Sum of A''s',
COUNT(DT.ID) AS 'Count of A''s'
FROM DemoTable DT
WHERE DT.GroupKey = 'A'
GROUP BY DT.StaticKey;

How can we return only unique records from table?

I am having a table structure like this
CREATE TABLE yourTable (
`Source` VARCHAR(20),
`Destination` VARCHAR(20),
`Distance` Integer
);
INSERT INTO yourTable
(`Source`, `Destination`, `Distance`)
VALUES
('Buffalo', 'Rochester', 2200),
('Yonkers', 'Syracuse', 1400),
('Cheektowaga', 'Schenectady', 600),
('Rochester', 'Buffalo', 2200)
How can we return only unique records for example as 'Buffalo' and 'Rochester' are present in 1 & 4 rows so one should be taken while retrieving.
I tried writing this query but here source and destination values are not correct for 3 rows Schenectady Cheektowaga
SELECT DISTINCT GREATEST(Source, Destination) as Source, LEAST(Source, Destination) AS Destination, Distance
FROM yourTable
Use two queries that you combine with UNION. One query returns the rows that are already unique, the other removes the duplicate from the rows that are duplicated in the other direction.
SELECT t1.Source, t1.Destination, t1.Distance
FROM yourTable AS t1
LEFT JOIN yourTable AS t2 ON t1.Source = t2.Destination AND t1.Destination = t2.Source
WHERE t2.Source IS NULL
UNION ALL
SELECT GREATEST(Source, Destination) AS s, LEAST(Source, Destination) AS d, MAX(Distance) AS Distance
FROM yourTable
GROUP BY s, d
HAVING COUNT(*) > 1
DEMO
Try this:
select * from yourTable group by greatest(source,destination);

Can you get a different column from a row with a MIN or MAX value?

I'm building an application with millions of rows, so I'm trying to avoid JOIN whenever possible. I have a table like this:
ID category value_1 value_2
1 1 2.2432 5.4321
2 2 6.5423 5.1203
3 1 8.8324 7.4938
4 2 0.4823 9.8244
5 2 7.2456 3.1278
6 1 1.9348 4.4421
I'm trying to retrieve value_1 from the row with the lowest ID and value_2 from the row with the highest ID while grouped by category, like this:
category value_1 value_2
1 2.2432 4.4421
2 6.5423 3.1278
Is this possible in an effective way while avoiding constructs like string operations and JOIN?
Thank you!
Try this:
SELECT
category,
(
SELECT t2.value1
FROM table1 t2
WHERE t2.id = MIN(t1.id)
) as value1,
(
SELECT t3.value2
FROM table1 t3
WHERE t3.id = MAX(t1.id)
) as value2
FROM
table1 t1
GROUP BY
category
;
Create and fill table:
CREATE TABLE `table1` (
`id` INT NOT NULL,
`category` INT NULL,
`value1` DOUBLE NULL,
`value2` DOUBLE NULL,
PRIMARY KEY (`id`)
);
INSERT INTO table1 VALUES
(1, 1, 2.2432, 5.4321),
(2, 2, 6.5423, 5.1203),
(3, 1, 8.8324, 7.4938),
(4, 2, 0.4823, 9.8244),
(5, 2, 7.2456, 3.1278),
(6, 1, 1.9348, 4.4421);
Output:
1 2.2432 4.4421
2 6.5423 3.1278
One approach which avoids joins is to use ROW_NUMBER:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY category ORDER BY ID) rn_min,
ROW_NUMBER() OVER (PARTITION BY category ORDER BY ID DESC) rn_max
FROM yourTable
)
SELECT
category,
MAX(CASE WHEN rn_min = 1 THEN value_1 END) AS value_1,
MAX(CASE WHEN rn_max = 1 THEN value_2 END) AS value_2
FROM cte
GROUP BY
category;
Demo
Edit:
The above query should benefit from the following index:
CREATE INDEX idx ON yourTable (category, ID);
This should substantially speed up the row number operations.

temporary table has a count for each other table

I'm trying to make a statistics page in my php script. in order to select the count from each table I need more than 30 Queries like this
SELECT COUNT(order_id) as `uncompleted_orders` FROM `orders` WHERE `order_status` != 0
and then I need to run another query like this:
SELECT COUNT(order_id) as `completed_orders` FROM `orders` WHERE `order_status` = 1
I've tried this approach, but it didn't work:
SELECT COUNT(order_id) as `uncompleted_orders` FROM `sd_orders` WHERE `order_status` != 4;
SELECT COUNT(order_id) as `completed_orders` FROM `sd_orders` WHERE `order_status` = 4;
Is there any way to creat a new temp table in MySQL contains the count for other tables?
You could try something like this:
SELECT
(
SELECT COUNT(order_id) FROM `sd_orders` WHERE `order_status` != 4
) as `uncompleted_orders`,
(
SELECT COUNT(order_id) FROM `sd_orders` WHERE `order_status` = 4
) as `completed_orders`
You will have a result set with one row and a field for each count.
Without more information it's impossible to generalise, but there are many constructs that can help you here.
First, your example is actually from one table, and not two. This means that you can do the following...
SELECT
COUNT(CASE WHEN order_status = 4 THEN order_id END) AS complete_orders,
COUNT(CASE WHEN order_status <> 4 THEN order_id END) AS incomplete_orders
FROM
sd_orders
This works because COUNT(<something>) doesn't include an NULLs in the results. And by not including an ELSE clause, anything that doesn't match returns NULL. Another way people accomplish the same result is SUM(CASE WHEN ? THEN 1 ELSE 0 END).
Second, where you do actually have multiple tables, you can combine the results in several different ways...
-- Where you want one value from each table...
--------------------------------------------------------------------------------
SELECT
(SELECT COUNT(*) FROM table1 WHERE fieldx = ?) AS value1,
(SELECT COUNT(*) FROM table2 WHERE fieldy = ?) AS value2
-- Where you want one row of values from each table...
--------------------------------------------------------------------------------
SELECT
table1_summary.value1 AS table1_value1,
table1_summary.value2 AS table1_value2,
table2_summary.value1 AS table2_value1,
table2_summary.value2 AS table2_value2
FROM
(
SELECT
COUNT(CASE WHEN fieldx = ? THEN id END) AS value1,
COUNT(CASE WHEN fieldx <> ? THEN id END) AS value2
FROM
table1
)
AS table1_summary
CROSS JOIN
(
SELECT
COUNT(CASE WHEN fieldy = ? THEN id END) AS value1,
COUNT(CASE WHEN fieldy <> ? THEN id END) AS value2
FROM
table2
)
AS table2_summary
-- Where you want many rows, but of the same fields, from each table...
--------------------------------------------------------------------------------
SELECT
*
FROM
(
SELECT
'Table1' AS source_table,
fielda AS some_grouping,
COUNT(CASE WHEN fieldx = ? THEN id END) AS value1,
COUNT(CASE WHEN fieldx <> ? THEN id END) AS value2
FROM
table1
GROUP BY
fielda
UNION ALL
SELECT
'Table2' AS source_table,
fieldb AS some_grouping,
COUNT(CASE WHEN fieldy = ? THEN id END) AS value1,
COUNT(CASE WHEN fieldy <> ? THEN id END) AS value2
FROM
table2
GROUP BY
fieldb
)
AS summary
ORDER BY
source_table,
some_grouping,
value1,
value2
As you can see, there are a lot of ways to do this. How you approach it totally depends on your data and your needs.

Count only null values in two different columns and show in one select statement

I want to count just the null values in a specific column and all the null values in another
specific column, however i want my result to have both of these results shown in one table.
Here is what i have so far:
Select Count(*) as 'Column1Count', Count(*) as 'Column2Count'
from table1
Where column1 is null
and column2 is null
please help
This should work :
select
(select count(*) from table1 where column1 is null) as 'Column1Count',
(select count(*) from table1 where column2 is null) as 'Column2Count';
You could use a case for that:
select sum(case when Column1 is null then 1 end) as Col1Count
, sum(case when Column2 is null then 1 end) as Col2Count
from table1