count same item between many rows and columns on one query - mysql

I have the next table:
id| C1 | C2 | C3 | C4 | C5|
----------------------------
01| 23 | 19 | 30 | 30 | 30|
---------------------------
02| 23 | 40 | 30 | 30 | 30|
----------------------------
03| 23 | 20 | 19 | 30 | 30|
----------------------------
04| 23 | 19 | 30 | 30 | 30|
----------------------------
05| 23 | 23 | 23 | 19 | 30|
----------------------------
in this table the count of number 19 is 4
I need a query to count how many times each number appears in the table, searching in many rows and columns, or at least number 19 with MySQL.

I'd union all the columns, and then just count the values:
SELECT c, COUNT(*)
FROM (SELECT c1 AS c FROM my_table
UNION ALL
SELECT c2 FROM my_table
UNION ALL
SELECT c3 FROM my_table
UNION ALL
SELECT c4 FROM my_table
UNION ALL
SELECT c5 FROM my_table) t
GROUP BY c

One option is to use IF with SUM:
SELECT SUM(IF(id=19,1,0) + IF(c1=19,1,0) + IF(c2=19,1,0) +
IF(c3=19,1,0) + IF(c4=19,1,0) + IF(c5=19,1,0))
FROM YourTable
Condensed SQL Fiddle Demo
As pointed out, the IF statement isn't actually needed:
SELECT SUM((c1=19)+(c2=19)+(c3=19)+(c4=19))
FROM YourTable

select count(*) FROM tablename where c1 = 19 OR c2 = 19 or c3 = 19 etc

I prefer the SUM method:
SELECT SUM(
(t1.c1=x.num)
+ (t1.c2=x.num)
+ (t1.c3=x.num)
+ (t1.c4=x.num)
+ (t1.c5=x.num)
) count
FROM table1 t1
JOIN (SELECT 19 num) x

Related

To find the last value in the dataset of 15 minutes interval

ID Timestamp Value
1 11:59.54 10
1 12.04.00 20
1 12.12.00 31
1 12.16.00 10
1 12.48.00 05
I want the result set as
ID Timestamp Value
1 11:59.54 10
1 12:00:00 10
1 12.04.00 20
1 12.12.00 31
1 12:15:00 31
1 12:16.00 10
1 12:30:00 10
1 12:45:00 10
1 12.48.00 05
More coffee will probably lead to a simpler solution, but consider the the following...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,timestamp TIMESTAMP
,value INT NOT NULL
);
INSERT INTO my_table VALUES
(1 ,'11:59:54',10),
(2 ,'12:04:00',20),
(3 ,'12:12:00',31),
(4 ,'12:16:00',10),
(5 ,'12:48:00',05);
... in addition, I have a table of integers, that looks like this:
SELECT * FROM ints;
+---+
| i |
+---+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
+---+
So...
SELECT a.timestamp
, b.value
FROM
( SELECT x.*
, MIN(y.timestamp) min_timestamp
FROM
( SELECT timestamp
FROM my_table
UNION
SELECT SEC_TO_TIME((i2.i*10+i1.i)*900)
FROM ints i1
, ints i2
WHERE SEC_TO_TIME((i2.i*10+i1.i)*900)
BETWEEN (SELECT MIN(timestamp) FROM my_table)
AND (SELECT MAX(timestamp) FROM my_table)
ORDER
BY timestamp
) x
LEFT
JOIN my_table y
ON y.timestamp >= x.timestamp
GROUP
BY x.timestamp
) a
JOIN my_table b
ON b.timestamp = min_timestamp;
+-----------+-------+
| timestamp | value |
+-----------+-------+
| 11:59:54 | 10 |
| 12:00:00 | 20 |
| 12:04:00 | 20 |
| 12:12:00 | 31 |
| 12:15:00 | 10 |
| 12:16:00 | 10 |
| 12:30:00 | 5 |
| 12:45:00 | 5 |
| 12:48:00 | 5 |
+-----------+-------+
The idea is as follows. Use SERIES_GENERATE() to generate the missing time stamps with the 15 minute intervals and and union it with the existing data your table T. Now you would want to use LAST_VALUE with IGNORE NULLS. IGNORE NULLS is not implemented in HANA, therefore you have to do a bit of a workaround. I use COUNT() as a window function to count the non null values. I do the same on the original data and then join both on the count. This way I repeat the last non-null value.
select X.ID, X.TIME, Y.VALUE from (
select ID, TIME, value,
count(VALUE) over (order by TIME rows between unbounded preceding and current row) as CNT
from (
--add the missing 15 minute interval timestamps
select 1 as ID, GENERATED_PERIOD_START as TIME, NULL as VALUE
from SERIES_GENERATE_TIME('INTERVAL 15 MINUTE', '12:00:00', '13:00:00')
union all
select ID, TIME, VALUE from T
)
) as X join (
select ID, TIME, value,
count(value) over (order by TIME rows between unbounded preceding and current row) as CNT
from T
) as Y on X.CNT = Y.CNT

Count total number of occurrence of two consecutive values in a table

My table structure
+----+--------+
| id | status |
+----+--------+
| 1 | 10 |
| 2 | 21 |
| 3 | 22 |
| 4 | 29 |
| 5 | 30 |
| 6 | 32 |
| 7 | 33 |
| 8 | 21 |
| 9 | 22 |
| 10 | 23 |
| 11 | 21 |
| 12 | 22 |
| 13 | 23 |
+----+--------+
I want to count total number of times when status 22 comes just after status 21.
In this case the query should return 3.
sql fiddle
Just use a Self Join with Conditional Aggregate
SELECT Sum(CASE WHEN a.status = 22 AND b.status = 21 THEN 1 END) As Stat_Count
FROM testTable a
LEFT OUTER JOIN testTable b
ON a.id = b.id + 1
SQLFIDDLE DEMO
If you can have gaps in your id's you can use a subquery to check whether the previous status of a 22 row is 21
select count(*)
from testtable a
where a.status = 22 and (select status from testtable b
where b.id < a.id order by id desc limit 1) = 21
http://sqlfiddle.com/#!2/9d567/2
Another way gets all id's of previous rows of rows with a status of 22 in derived table and then joins the ids to count how many have a status of 21
select count(*) from (
select max(b.id) max_b_id
from testtable a join testtable b on b.id < a.id
where a.status = 22
group by a.id
) t1 join testtable a on a.id = t1.max_b_id
where a.status = 21
I have tried to solve it in php
$q="";
$q= mysqli_query("select *from testTable");
while($r=mysqli_fetch_assoc($q)){
$rows[]=$r;
}
$success=0;
for ($i=0;$i<count($rows);$i++){
if($rows[$i]['status']==21 and $rows[$i+1]['status']==22 ){
$success+=1;
}
}
echo $success;

Split data in to ranges and display count

I am not an expert in MySql. I am trying to split the data in my table in to ranges based on account_no. This is my table.
mysql> select * from manager;
+----+-------+------------+
| id | name | account_no |
+----+-------+------------+
| 1 | John | 5 |
| 2 | Peter | 15 |
| 3 | Tony | 18 |
| 4 | Mac | 35 |
| 5 | Max | 55 |
| 6 | Smith | 58 |
+----+-------+------------+
As you see the account_no is a positive number. I want to split these records in to batches of 10, based on account_no and display the count in that range.
For an example
between 0 and 10 there is only 1 record
between 11 and 20 there are 2 records
between 21 and 30 there are no records*(So this should be omitted.)*
etc...
Actually I am hoping to get an output like this.
+-------------+-----------+-------+
| range_start | range_end | count |
+-------------+-----------+-------+
| 1 | 10 | 1 | -> because there is 1 record between 1 and 10
| 11 | 20 | 2 | -> because there are 2 records between 11 and 20
| 31 | 40 | 1 | -> because there is 1 record between 31 and 40
| 51 | 60 | 2 | -> because there are 2 records between 51 and 60
+-------------+-----------+-------+
I tried several combinations but all of them give me only one row in the result. Can anybody help me?
My suggestion would be to create a table that contains the ranges - startRange and endRange:
CREATE TABLE range_values (`startRange` int, `endRange` int) ;
INSERT INTO range_values(`startRange`, `endRange`)
VALUES (1, 10), (11, 20), (21, 30), (31, 40), (51, 60);
Once the table is created, then you can easily join on the table to get the count.
select r.startRange,
r.endRange,
count(m.account_no) totalCount
from manager m
inner join range_values r
on m.account_no >=startrange
and m.account_no <= endrange
group by r.startRange, r.endRange
See SQL Fiddle with Demo.
The benefit of the table is that you are not coding the range values and can easily updated the ranges in a table without having to change your code.
This query return:
| STARTRANGE | ENDRANGE | TOTALCOUNT |
--------------------------------------
| 1 | 10 | 1 |
| 11 | 20 | 2 |
| 31 | 40 | 1 |
| 51 | 60 | 2 |
If you don't want to create a new table, then you can use something similar to the following:
select startrange,
endrange,
count(m.account_no) TotalCount
from manager m
inner join
(
select 1 startRange, 10 endrange union all
select 11 startRange, 20 endrange union all
select 21 startRange, 30 endrange union all
select 31 startRange, 40 endrange union all
select 41 startRange, 50 endrange union all
select 51 startRange, 60 endrange
) r
on m.account_no >=startrange
and m.account_no <= endrange
group by r.startRange, r.endRange
See SQL Fiddle with demo
This should give you the output you would like, and includes the ranges with zero in the count column.
SET #rs = 0; SELECT IF(#rs, #rs := #rs + 10, #rs := 1) AS range_start, #rs + 9 AS range_end, (SELECT COUNT(id) FROM manager WHERE account_no >= #rs AND account_no <= #rs + 9) AS `count` FROM manager;
To omit the rows with zero in the count column;
SET #rs = 0; SELECT * FROM (SELECT IF(#rs, #rs := #rs + 10, #rs := 1) AS range_start, #rs + 9 AS range_end, (SELECT COUNT(id) FROM manager WHERE account_no >= #rs AND account_no <= #rs + 9) AS `count` FROM manager) AS data WHERE `count` > 0;
Try with something like this:
SELECT
CASE
WHEN range_start < 10 THEN 'Under 10'
WHEN range_start BETWEEN 11 and 29 THEN '11 - 29'
(...more ranges...)
END as range,
COUNT(*) AS count
GROUP BY range
ORDER BY range
You should use functions similar to rank and dense_rank in MSSQL, you can implement them in MySQL starting at the following link:
http://www.folkstalk.com/2013/03/grouped-dense-rank-function-mysql-sql-query.html

To select matching strings in 2 tables

TableA
Id | M | D | Y |
=======================
1 | 10 | 28 | 2012 |
2 | 11 | 29 | 2012 |
3 | 12 | 30 | 2012 |
TableB
Id | M | D | Y |
=======================
4 | 09 | 28 | 2012 |
5 | 11 | 29 | 2012 |
6 | 01 | 30 | 2013 |
I will search by M and D
Ex: If matching M = 11 AND D = 29 ... so will return ID(s) 2 , 5
I can only find by one table like this
mysql_query("SELECT * FROM TableA WHERE M='11' AND Y='29' ORDER BY D ASC , Id DESC;";)
But how to find in multiple tables ?
Use the UNION clause between two SELECTs
(SELECT Id, M, D, Y FROM TableA WHERE M='11' AND Y='29')
UNION
(SELECT Id, M, D, Y FROM TableB WHERE M='11' AND Y='29')
ORDER BY D ASC , Id DESC
You better use UNION ALL (in case tableB has identical records) to get all the records. Otherwise just UNION will do.
SELECT * FROM TableA WHERE M='11' AND Y='29'
UNION ALL
SELECT * FROM TableB WHERE M='11' AND Y='29'
ORDER BY D ASC , Id DESC;
SELECT * FROM TableA WHERE M='11' AND Y='29'
UNION
SELECT * FROM TableB WHERE M='11' AND Y='29'
ORDER BY D ASC , Id DESC;

MySQL left join with a single table

Using the following query to get a hourly breakdown of transactions
SELECT hour(Stamp) AS Hour, count(1) AS Count FROM Transactions GROUP by 1 WITH ROLLUP;
Results in the following output:
+------+-------+
| Hour | Count |
+------+-------+
| 0 | 269 |
| 1 | 342 |
| 2 | 319 |
| 3 | 284 |
| 4 | 235 |
| 5 | 174 |
| 6 | 91 |
| 7 | 54 |
| 8 | 31 |
| 9 | 21 |
| 10 | 21 |
| 11 | 1 |
| NULL | 1842 |
+------+-------+
I would like to display the hours with 0 transactions (e.g. in this example, every hour between 12 and 23 would show '0'). What would be the simplest way to do this?
try something like this (the -1 hour_id is for the rollup total):
drop table if exists hours;
create table hours(hour_id tinyint primary key) engine=innodb;
insert into hours (hour_id) values
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),
(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23),(0),(-1);
select
h.hour_id,
if(t.counter is null, 0, t.counter) as counter
from
hours h
left outer join
(
select
if(hour(stamp) is null, -1, hour(stamp)) as hour_id,
count(stamp) as counter
from
transactions group by stamp with rollup
) t
on h.hour_id = t.hour_id;
if you want it by months create a months table 1..12 + -1 etc...
SELECT Temp.Hour, COUNT(1)
FROM (
SELECT 0 AS Hour UNION
SELECT 1 UNION
SELECT 2 UNION
SELECT 3 UNION
SELECT 4 UNION
SELECT 5 UNION
SELECT 6 UNION
SELECT 7 UNION
SELECT 8 UNION
SELECT 9 UNION
SELECT 10 UNION
SELECT 11 UNION
SELECT 12 UNION
SELECT 13 UNION
SELECT 14 UNION
SELECT 15 UNION
SELECT 16 UNION
SELECT 17 UNION
SELECT 18 UNION
SELECT 19 UNION
SELECT 20 UNION
SELECT 21 UNION
SELECT 22 UNION
SELECT 23
) AS Temp
LEFT JOIN Transactions
ON Temp.Hour = HOUR(Transactions.Stamp)
GROUP BY Temp.Hour
ORDER BY Temp.Hour