mysql count zeros in sequence - mysql

I got mysql database and I need to get number of zeros in sequence and print them all with date from first zero, so for example I got a table like this
id, date, impuls_count
1, '12-05-15 12:00:00', 60
2, '12-05-15 12:01:00', 0
3, '12-05-15 12:02:00', 0
4, '12-05-15 12:03:00', 49
5, '12-05-15 12:04:00', 0
6, '12-05-15 12:05:00', 0
7, '12-05-15 12:06:00', 0
8, '12-05-15 12:07:00', 0
9, '12-05-15 12:08:00', 30
10, '12-05-15 12:09:00', 0
this should give the result like this:
'12-05-15 12:01:00', 2
'12-05-15 12:04:00', 4
'12-05-15 12:09:00', 1
I tried to solve it on my own but my query works very slow(I got 5000 rows in a table) and it sometimes prints same row twice
SELECT qwe.date, ile
FROM (SELECT p.date,
(SELECT COUNT(*)
FROM performance_v2
WHERE date > p.date
AND date <
(SELECT MIN(date)
FROM performance_v2
WHERE date > p.date AND impuls_count > 0)) ile
FROM performance_v2 p
WHERE p.impuls_count > 0
AND (date(p.date)
BETWEEN '2015-05-08%'
AND '2015-05-08%')
AND (time(p.date)
between '14:00:00' and '22:00:00')
ORDER BY 1) qwe
WHERE ile > 0

In MySQL, this is easiest to solve using variables. The idea is to have a counter increment each time the value of impuls_count changes. This defines groups of common values. You can then filter the values and aggregate to get what you want:
select min(date), count(*)
from (select t.*,
(#g := if(#ic = impuls_count, #g,
if(#ic := impuls_count, #g + 1, #g + 1)
)
) as grp
from table t cross join
(select #ic := 0, #g := 0)
order by id
) t
where impuls_count = 0
group by grp

Related

loop over a date list (or any list) and append queries in mysql or snowflake

I am new to sql language and recently snowflake. I have a table that contains all checkin dates for all users for a business
user_id | checkin_date
001 03-06-2018
001 07-07-2018
001 08-01-2018
002 03-19-2018
002 03-27-2018
002 07-11-2018
Now I want to do a query such that I can look back from a query_date to see how many times each user checked in between query_date - 7 and query_date, qyery_date - 90 and query date ... the following snowflake query does the job properly for query_date='2018-08-01'.
with user_checkin_history_sum as (
select
user_id,
sum(iff(datediff(DAY, uc.checkin_date, '2018-08-01') <= 7, 1, 0)) as visits_past_7_days,
sum(iff(datediff(DAY, uc.checkin_date, '2018-08-01') <= 90, 1, 0)) as visits_past_90_days,
from user_checkin as uc
where uc.checkin_date < '2018-08-01'
group by user_id
order by user_id
)
This gives me result
user_id | visits_past_7_days | visits_past_90_days
001 0 2
002 0 1
My question is, if I have more than one day as the query_date, i.e., I have a list of checkin_date, for each checkin_date in the list, I do the query as above and append all them together. Basically, it is a loop over + table append, but I do not find an answer how to do this in sql language. Essentially, what I want to do is like the following
with user_checkin_history_sum as (
select
user_id,
sum(iff(datediff(DAY, uc.checkin_date, query_date) <= 7, 1, 0)) as visits_past_7_days,
sum(iff(datediff(DAY, uc.checkin_date, query_date) <= 90, 1, 0)) as visits_past_90_days,
from user_checkin as uc
where uc.checkin_date < query_date and
LOOP OVER
query_date in ('2018-08-01', '2018-06-01')
group by user_id
order by user_id
)
And hopefully it gives this result
user_id | query_date | visits_past_7_days | visits_past_90_days
001 '08-01-2018' 0 2
002 '08-01-2018' 0 1
001 '06-01-2018' 0 1
002 '06-01-2018' 0 2
You should be able to cross join a table containing all the dates you want to examine:
WITH dates AS (
SELECT '2018-06-01' AS query_date UNION ALL
SELECT '2018-08-01' UNION ALL
... -- maybe other dates as well
),
user_checkin_history_sum AS (
SELECT
uc.user_id,
d.query_date,
SUM(IFF(DATEDIFF(DAY, uc.checkin_date, d.query_date) <= 7, 1, 0)) AS visits_past_7_days,
SUM(IFF(DATEDIFF(DAY, uc.checkin_date, d.query_date) <= 90, 1, 0)) AS visits_past_90_days
FROM dates d
CROSS JOIN user_checkin AS uc
WHERE uc.checkin_date < '2018-08-01'
GROUP BY d.query_date, uc.user_id
ORDER BY d.query_date, uc.user_id
)

Extract numeric part of string and get max value in column

I have a table foo that stores codes in format lnnnnn where l is at least one letter and n is numeric value. Both letters or numbers can be of various length, so trying to solve this like mentioned here won't work.
Example:
group | code
=============
1 | a0010
1 | a0012
1 | a0013
2 | bn0014
2 | bn0015
2 | bn0016
3 | u0017
3 | u0018
My task is to get current highest numeric value of this column in desired group, to generate new number (like sequence).
Note that I cannot redesign table and explode string and text parts.
So far I tried:
select
max(code rlike '[0-9]$')
from
foo
where
group = 2
but, sadly, regexp or rlike (synonyms) returns only 0 or 1 (matched or not matched).
One method is a brute force method:
select grp,
max(case when substr(code, 1, 1) between '0' and '9' then code + 0
when substr(code, 2, 1) between '0' and '9' then substr(code, 2) + 0
when substr(code, 3, 1) between '0' and '9' then substr(code, 3) + 0
when substr(code, 4, 1) between '0' and '9' then substr(code, 4) + 0
when substr(code, 5, 1) between '0' and '9' then substr(code, 5) + 0
when substr(code, 6, 1) between '0' and '9' then substr(code, 6) + 0
when substr(code, 7, 1) between '0' and '9' then substr(code, 7) + 0
when substr(code, 8, 1) between '0' and '9' then substr(code, 8) + 0
end)
from foo
group by grp;
If your numeric codes is always four digits then you can do it like:
select groupid, max(right(code,4)) as maxcode
from foo
group by groupid
See it here on fiddle: http://sqlfiddle.com/#!2/775b3/2
If all numeric parts start with a 0:
select gp, max(cast(substr(code, instr(code, '0')) as unsigned))
from t
group by gp
See sqlfiddle
If not, for arbitrary numeric parts (that start with any digit):
select gp, max(cast(substr(code, instr(code, n)) as unsigned))
from t
join (select 0 n 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) x
group by gp
See sqlfiddle

SQL Select Query Help. Maximum sum of consequtive four rows.

We have a traffic counter that counts cars in each lane (two inbound and two outbound) in 15 minute increments.
There is a peak period which is defined as 7:00am to 9:00am. Within this peak period we want to know the PeakHourIn and PeakHourOut and PeakHourSum.
The PeakHourIn is the highest consecutive 4x15 minute total (1 hour) for lne1in + lne4in
The PeakHourOut is the highest consecutive 4x15 minute total (1 hour) for lne2out + lne3out
The PeakHourSum is the highest consecutive 4x15 minute total (1 hour) for all lanes.
Date Time lne1in lne2out lne3out lne4in
09-18-2012 5:45 AM 2 0 0 0
09-18-2012 6:00 AM 1 0 0 1
09-18-2012 6:15 AM 2 1 0 0
09-18-2012 6:30 AM 2 1 0 0
09-18-2012 6:45 AM 6 1 2 1
09-18-2012 7:00 AM 9 1 0 3
09-18-2012 7:15 AM 81 12 22 15
09-18-2012 7:30 AM 144 31 63 56
09-18-2012 7:45 AM 84 30 62 42
09-18-2012 8:00 AM 7 1 0 3
09-18-2012 8:15 AM 11 2 3 3
09-18-2012 8:30 AM 12 3 7 1
09-18-2012 8:45 AM 16 4 8 0
09-18-2012 9:00 AM 5 2 5 0
09-18-2012 9:15 AM 10 1 4 0
Results should look like:
PeakHourIn 434
PeakHourOut 221
PeakHourSum 655
Any help would be greatly appreciated.
If you used a native temporal data type to store the date/time, you could group multiple self-joins:
SELECT MAX(lne1in + lne4in ) AS PeakHourIn,
MAX(lne2out + lne3out) AS PeakHourOut,
MAX(lne1in + lne2out + lne3out + lne4in) AS PeakHourSum
FROM (
SELECT t1.lne1in + t2.lne1in + t3.lne1in + t4.lne1in AS lne1in,
t1.lne2out + t2.lne2out + t3.lne2out + t4.lne2out AS lne2out,
t1.lne3out + t2.lne3out + t3.lne3out + t4.lne3out AS lne3out,
t1.lne4in + t2.lne4in + t3.lne4in + t4.lne4in AS lne4in
FROM my_table t1
JOIN my_table t2 ON t2.DateTime = t1.DateTime + INTERVAL 15 MINUTE
JOIN my_table t3 ON t3.DateTime = t2.DateTime + INTERVAL 15 MINUTE
JOIN my_table t4 ON t4.DateTime = t3.DateTime + INTERVAL 15 MINUTE
WHERE TIME(t1.DateTime) BETWEEN '07:00:00' AND '08:00:00'
GROUP BY t1.DateTime
) t
EDIT
Here's a solution in MySQL: http://sqlfiddle.com/#!2/ff0fb/9
create table TrafficData
(
StartTime timestamp
,Lane int
,CarCount int
);
create table LaneData
(
Lane int
, Direction bit
);
insert LaneData
select 1, 0
union select 2, 1
union select 3, 1
union select 4, 0;
insert TrafficData
select dt, lane
, case lane
when 1 then l1
when 2 then l2
when 3 then l3
when 4 then l4
else null
end
from
(
select '2012-09-18 05:45' dt, 2 l1, 0 l2, 0 l3, 0 l4
union all select '2012-09-18 06:00', 1, 0, 0, 1
union all select '2012-09-18 06:15', 2, 1, 0, 0
union all select '2012-09-18 06:30', 2, 1, 0, 0
union all select '2012-09-18 06:45', 6, 1, 2, 1
union all select '2012-09-18 07:00', 9, 1, 0, 3
union all select '2012-09-18 07:15', 81, 12, 22, 15
union all select '2012-09-18 07:30', 144, 31, 63, 56
union all select '2012-09-18 07:45', 84, 30, 62, 42
union all select '2012-09-18 08:00', 7, 1, 0, 3
union all select '2012-09-18 08:15', 11, 2, 3, 3
union all select '2012-09-18 08:30', 12, 3, 7, 1
union all select '2012-09-18 08:45', 16, 4, 8, 0
union all select '2012-09-18 09:00', 5, 2, 5, 0
union all select '2012-09-18 09:15', 10, 1, 4, 0
) as originalTable
cross join LaneData;
select Lane, max(SumCarCount) as MaxSumCarCount
from
(
select a.Lane, SUM(b.CarCount) as SumCarCount
from TrafficData a
inner join TrafficData b
on b.Lane = a.Lane
and b.StartTime between a.StartTime and DATE_ADD(DATE_ADD(a.starttime, interval 1 hour), interval -1 second)
where time(a.StartTime) between '07:00' and '08:15'
group by a.Lane, a.StartTime
) x
group by Lane
order by Lane;
select Direction, max(SumCarCount) as MaxSumCarCount
from
(
select al.Direction, SUM(b.CarCount) SumCarCount
from TrafficData a
inner join LaneData al
on al.Lane = a.Lane
inner join TrafficData b
on b.StartTime between a.StartTime and DATE_ADD(DATE_ADD(a.starttime, interval 1 hour), interval -1 second)
inner join LaneData bl
on bl.Lane = b.Lane
and bl.Direction = al.Direction
where time(a.StartTime) between '07:00' and '08:15'
group by al.Direction, a.StartTime
) x
group by Direction
order by Direction;
ORIGINAL
Here's how I'd go about it in SQL Server:
--I'd change your table structure to be like this - that way you can easily add new lanes without rewriting the whole system
declare #trafficData table
(
StartTime DateTime
,Lane int
,CarCount int
)
--here's where you store additional info about the lanes (e.g. what direction they go in)
declare #laneData table
(
Lane int
, Direction bit --0 in, 1 out
)
--populate the tables with sample data
insert #laneData
select 1, 0
union select 2, 1
union select 3, 1
union select 4, 0
insert #trafficData
select dt, lane
, case lane
when 1 then l1
when 2 then l2
when 3 then l3
when 4 then l4
else null --should never happen
end
from
(
select '2012-09-18 5:45 AM' dt, 2 l1, 0 l2, 0 l3, 0 l4
union all select '2012-09-18 6:00 AM', 1, 0, 0, 1
union all select '2012-09-18 6:15 AM', 2, 1, 0, 0
union all select '2012-09-18 6:30 AM', 2, 1, 0, 0
union all select '2012-09-18 6:45 AM', 6, 1, 2, 1
union all select '2012-09-18 7:00 AM', 9, 1, 0, 3
union all select '2012-09-18 7:15 AM', 81, 12, 22, 15
union all select '2012-09-18 7:30 AM', 144, 31, 63, 56
union all select '2012-09-18 7:45 AM', 84, 30, 62, 42
union all select '2012-09-18 8:00 AM', 7, 1, 0, 3
union all select '2012-09-18 8:15 AM', 11, 2, 3, 3
union all select '2012-09-18 8:30 AM', 12, 3, 7, 1
union all select '2012-09-18 8:45 AM', 16, 4, 8, 0
union all select '2012-09-18 9:00 AM', 5, 2, 5, 0
union all select '2012-09-18 9:15 AM', 10, 1, 4, 0
) originalTable
cross join #laneData
--peak for each individual lane
select *
from
(
select a.Lane, a.StartTime, SUM(b.CarCount) SumCarCount
, ROW_NUMBER() over (partition by a.lane order by SUM(b.CarCount) desc) r
from #trafficData a
inner join #trafficData b
on b.Lane = a.Lane
and b.StartTime between a.StartTime and DATEADD(second,-1,DATEADD(hour,1,a.starttime))
group by a.Lane, a.StartTime
) x
where r = 1
order by Lane
--peak for lane direction
select *
from
(
select al.Direction, a.StartTime, SUM(b.CarCount) SumCarCount
, ROW_NUMBER() over (partition by al.Direction order by SUM(b.CarCount) desc) r
from #trafficData a
inner join #laneData al
on al.Lane = a.Lane
inner join #trafficData b
on b.StartTime between a.StartTime and DATEADD(second,-1,DATEADD(hour,1,a.starttime))
inner join #laneData bl
on bl.Lane = b.Lane
and bl.Direction = al.Direction
group by al.Direction, a.StartTime
) x
where r = 1
order by Direction

Mysql Ranking on on Left JOIN

I have two tables (teams and matches) and I want calculate the game table using points and #rank position.
The tables are very simple: Teams and matches (battles). The last column in 'battle' means if the battle was played or not.
DROP TABLE IF EXISTS monsters;
DROP TABLE IF EXISTS battles;
CREATE TABLE monsters (id_monster int auto_increment primary key, monster varchar(50));
CREATE TABLE battles (id_battle int auto_increment primary key, monster_h int, monster_a int, score_a int, score_h int, played tinyint);
Here some fake data
(Edit: I've updated the script removing random score and apply fixed scored.)
INSERT INTO monsters (monster) VALUES ("Nembo"), ("Kid"), ("Captain"), ("OOI"), ("Koky"), ("Rudes");
INSERT INTO battles (monster_h, monster_a, score_h, score_a, played) VALUES
(1,2, 3, 2,1),
(2,1, 2, 2,1),
(3,4, 1, 0,1),
(4,3, 0, 1,1),
(5,6, 0, 0,0),
(6,5, 5, 2,1),
(1,3, 6, 0,1),
(3,1, 2, 7,1),
(2,5, 1, 1,1),
(6,4, 0, 0,0),
(4,6, 0, 0,0);
Here my question
As you can see the Points are calculated correctly and table is good! But... #rank I wrong! I think I should join it too. Can you help me on #rank ?
SELECT m.monster,
(SUM(CASE WHEN
(b.monster_h = m.id_monster AND b.score_h > b.score_a) OR
(b.monster_a = m.id_monster AND b.score_a > b.score_h) THEN 3 ELSE 0 END) +
SUM(CASE WHEN
(b.monster_h = m.id_monster OR b.monster_a = m.id_monster) AND b.score_a = b.score_h THEN 1 ELSE 0 END)
) as pt,
(SUM(CASE WHEN
(b.monster_h= m.id_monster OR b.monster_a= m.id_monster) THEN 1 ELSE 0 END) ) as pl,
(#rank := #rank + 1) AS rank
FROM ( monsters as m )
LEFT JOIN battles as b ON m.id_monster IN (b.monster_a,b.monster_h)
,( SELECT #rank := 0 ) AS vars
where b.played = 1
GROUP BY m.monster
order by pt DESC, rand();
The result: with a wrong #rank
Monster, PTs, BATTLES, #RANK (good rank should be)
Nembo, 10, 4, 1 #1
Captain, 6, 4, 3 #2
Rudes, 3, 1, 6 #3
Kid, 2, 3, 2 #4
Koky, 1, 2, 5 #5
OOI, 0, 2, 4 #6
UPDATE
Use this data to have more monsters with same points and scores:
(1,2, 3, 2,1),
(2,1, 2, 2,1),
(3,4, 3, 0,1),
(4,3, 0, 1,1),
(5,6, 0, 0,0),
(6,5, 16, 12,1),
(1,3, 6, 0,1),
(3,1, 2, 7,1),
(2,5, 1,1,1),
(6,4, 0, 0,0),
(4,6, 0, 1,1),
(4,2,1,0,1),
(4,5,5,0,1),
(2,3,2,0,1),
(2,1,1,1,1);
and then use this script as Stefan said:
SELECT *, (#rank := #rank + 1) AS rank
FROM ( SELECT #rank := 0 ) as r ,
(SELECT m.monster,
(SUM(CASE WHEN
(b.monster_h = m.id_monster AND b.score_h > b.score_a) OR
(b.monster_a = m.id_monster AND b.score_a > b.score_h) THEN 3 ELSE 0 END) +
SUM(CASE WHEN
(b.monster_h = m.id_monster OR b.monster_a = m.id_monster) AND
b.score_a = b.score_h THEN 1 ELSE 0 END)) as pt,
(SUM(CASE WHEN
(b.monster_h= m.id_monster OR b.monster_a= m.id_monster) THEN 1 ELSE 0 END)) as pl,
(SUM(CASE WHEN
(b.monster_h= m.id_monster ) THEN b.score_h ELSE 0 END) +
SUM(CASE WHEN
(b.monster_a= m.id_monster ) THEN b.score_a ELSE 0 END)) as scored
, floor(rand()*1000) as coin
FROM monsters as m
LEFT JOIN battles as b ON m.id_monster IN (b.monster_a,b.monster_h)
where b.played = 1
GROUP BY m.monster) as result
order by result.pt DESC, scored DESC , coin DESC
The table is ordered by POINTS as first criteria, then scored. And it works. But if I use the "coin" as 3th criteria.... it doesn't works!
Great that you included all scripts to recreate your situation.
Put your results from your current query into a subquery, the trick with the #rank doesn't work with GROUP BY.
With this script I get the correct results:
SELECT *, (#rank := #rank + 1) AS rank
FROM ( SELECT #rank := 0 ) as r ,
(SELECT m.monster,
(SUM(CASE WHEN
(b.monster_h = m.id_monster AND b.score_h > b.score_a) OR
(b.monster_a = m.id_monster AND b.score_a > b.score_h) THEN 3 ELSE 0 END) +
SUM(CASE WHEN
(b.monster_h = m.id_monster OR b.monster_a = m.id_monster) AND
b.score_a = b.score_h THEN 1 ELSE 0 END)) as pt,
(SUM(CASE WHEN
(b.monster_h= m.id_monster OR b.monster_a= m.id_monster) THEN 1 ELSE 0 END)) as pl
FROM monsters as m
LEFT JOIN battles as b ON m.id_monster IN (b.monster_a,b.monster_h)
where b.played = 1
GROUP BY m.monster) as result
order by result.pt DESC

Creating summary report from multiple fields from a set of data

I have a task to write an sql to summarize the following data:
select
payment,
status,
interest,
principal
from payment
payment status interest principal
1 A 100 0
2 r 0 500
3 o 0 400
4 d 0 100
5 A 0 200
6 A 200 0
7 A 300 0
8 A 0 300
a payment relating to interest would be where interest is not 0
a payment relating to principal would be where a principal is not 0
the data would need to be split into type
The result I am looking for is similar to this:
Interest total count principal total count
Status - A 3 1
Other 0 4
total of all payments 3 5
I have been using group function but have not been able to group beyond the interest amount. Any suggestions would be greatly appreciated.
I would suggest to use GROUP BY + WITH ROLLUP for this. Something like:
SELECT
IF(`status` = 'A', 'Status A', 'Others') as `-`,
SUM(`interest` > 0) as `Interest total count`,
SUM(`principal` > 0) as `principal total count`
FROM
(
SELECT 1 as `payment`, 'A' as `status`, 100 as `interest`, 0 as `principal`
UNION ALL
SELECT 2, 'r', 0, 500
UNION ALL
SELECT 3, 'o', 0, 400
UNION ALL
SELECT 4, 'd', 0, 100
UNION ALL
SELECT 5, 'A', 0, 200
UNION ALL
SELECT 6, 'A', 200, 0
UNION ALL
SELECT 7, 'A', 300, 0
UNION ALL
SELECT 8, 'A', 0, 300
) as `sub`
GROUP BY 1 WITH ROLLUP;
Result:
- Interest total count principal total count
Others 0 3
Status A 3 2
3 5
Query:
SELECT
IF(`status` = 'A', 'Status A', 'Others') as `-`,
SUM(`interest` > 0) as `Interest total count`,
SUM(`principal` > 0) as `principal total count`
FROM
`payment`
GROUP BY 1 WITH ROLLUP;