select sum until a specific line is found - mysql

CREATE TABLE Coins (Badge VARCHAr(10), Cointype INT, Number INT);
INSERT INTO Coins (Badge,Cointype, Number) VALUES
('' , 1, 1),
('' , 5, 2),
('' , 20, 3),
('' , 1, 4),
('' , 5, 5),
('' , 5, 6),
('' , 5, 7),
('RESET', 0 , 0),
('' , 1, 8),
('' , 10, 9),
('RESET', 0 , 0),
('' , 5, 10),
('' , 20, 11);
Hi, with this table I want to have the following sum above the first line ('RESET',0,0)
select
SUM(case when Cointype=1 Then Number else 0 END) as SUM1,
SUM(case when Cointype=5 Then Number else 0 END) as SUM5,
SUM(case when Cointype=20 Then Number else 0 END) as SUM20
from Coins
result must be this:
SUM1 SUM5 SUM20
5 20 3
Is this possible in MySql?
in the real situation, there is also an ID and timestamp field. the top record is the most recent record with the highest ID and timestamp.
So the most recent records must summarized until the RESET line. (in case we need ordering, this is possible with the ID or timestamp field)
Then the table is like this:
CREATE TABLE Coins (ID INT, Badge VARCHAr(10), Cointype INT, Number INT);
INSERT INTO Coins (ID, Badge,Cointype, Number) VALUES
(13,'' , 1, 1),
(12,'' , 5, 2),
(11,'' , 20, 3),
(10,'' , 1, 4),
(9,'' , 5, 5),
(8,'' , 5, 6),
(7,'' , 5, 7),
(6,'RESET', 0 , 0),
(5,'' , 1, 8),
(4,'' , 10, 9),
(3,'RESET', 0 , 0),
(2,'' , 5, 10),
(1,'' , 20, 11);

If you have an autonumeric column:
SQL DEMO
select
SUM(case when Cointype=1 Then Number else 0 END) as SUM1,
SUM(case when Cointype=5 Then Number else 0 END) as SUM5,
SUM(case when Cointype=20 Then Number else 0 END) as SUM20
from Coins
WHERE ID < ( SELECT MIN(ID)
FROM Coins
WHERE Badge = 'RESET' )
OUTPUT
| SUM1 | SUM5 | SUM20 |
|------|------|-------|
| 5 | 20 | 3 |
EDIT:
After you change your sample looks like you have the rows in inverse order. Then you need MAX() instead of MIN()
select
SUM(case when Cointype=1 Then Number else 0 END) as SUM1,
SUM(case when Cointype=5 Then Number else 0 END) as SUM5,
SUM(case when Cointype=20 Then Number else 0 END) as SUM20
from Coins
WHERE ID > ( SELECT MAX(ID)
FROM Coins
WHERE Badge = 'RESET' )

Related

MySQL - Calculate Revenue per employee per month

I am trying to calculate from my table "Revenue per employee"(how much money each employee generates for the company) per month.
My code So far:
SELECT
y.yr,
d.details,
d.labelname,
sum(case when month(app_date) = 1 then val else 0 end) month_01,
sum(case when month(app_date) = 2 then val else 0 end) month_02,
sum(case when month(app_date) = 3 then val else 0 end) month_03,
sum(case when month(app_date) = 4 then val else 0 end) month_04,
sum(case when month(app_date) = 5 then val else 0 end) month_05,
sum(case when month(app_date) = 6 then val else 0 end) month_06,
sum(case when month(app_date) = 7 then val else 0 end) month_07,
sum(case when month(app_date) = 8 then val else 0 end) month_08,
sum(case when month(app_date) = 9 then val else 0 end) month_09,
sum(case when month(app_date) = 10 then val else 0 end) month_10,
sum(case when month(app_date) = 11 then val else 0 end) month_11,
sum(case when month(app_date) = 12 then val else 0 end) month_12,
sum(case when month(app_date) > 0 then val else 0 end) total
from (
select 'a' dorder,'Peter' labelname,'1' details union all
select 'b' dorder,'John + Mary' labelname,'2' details union all
select 'c' dorder,'John' labelname,'3' details
) d cross join (
select distinct year(app_date) yr
from tblapp
) y
left join (
SELECT app_date, COALESCE(app_price, 0) val, '1' details from tblapp
INNER JOIN tblemployee ON tblemployee.emp_id = tblapp.emp_id
where tblemployee.emp_id=1
union all
SELECT app_date, COALESCE(app_price, 0) val, '2' details from tblapp
INNER JOIN tblemployee ON tblemployee.emp_id = tblapp.emp_id
INNER JOIN tblassistant ON tblassistant.ass_id = tblapp.ass_id
where tblemployee.emp_id=2 AND tblassistant.ass_id=1
union all
SELECT app_date, COALESCE(app_price, 0) val, '3' details from tblapp
INNER JOIN tblemployee ON tblemployee.emp_id = tblapp.emp_id
where tblemployee.emp_id=2
) t on year(t.app_date) = y.yr and t.details = d.details
group by y.yr, d.details
order by y.yr desc, d.dorder;
Code is working fine. Only problem is when i add a new employee or assistant, i have to edit the code and create manually the WHERE conditions(groups between employee and assistant)
CREATE TABLE `tblapp` (
`app_id` smallint(5) UNSIGNED NOT NULL,
`app_date` date DEFAULT NULL,
`app_price` double DEFAULT NULL,
`emp_id` smallint(5) UNSIGNED DEFAULT NULL,
`ass_id` smallint(5) UNSIGNED DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `tblapp` (`app_id`, `app_date`, `app_price`, `emp_id`, `ass_id`) VALUES
(1, '2021-01-04', 100, 1, NULL),
(2, '2021-01-29', 100, 5, 1),
(3, '2021-02-20', 100, 2, 1),
(4, '2021-02-02', 100, 3, 2),
(5, '2021-03-19', 100, 2, NULL),
(6, '2021-04-24', 100, 4, 2),
(7, '2021-05-09', 100, 1, 1),
(8, '2021-07-04', 100, 2, 2),
(9, '2021-09-18', 100, 3, 1),
(10, '2021-10-12', 100, 5, NULL);
CREATE TABLE `tblemployee` (
`emp_id` smallint(5) UNSIGNED NOT NULL,
`emp_name` varchar(50) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `tblemployee` (`emp_id`, `emp_name`) VALUES
(1, 'Peter'),
(2, 'John'),
(3, 'Alex'),
(4, 'Stack'),
(5, 'Over'),
(6, 'Flow');
CREATE TABLE `tblassistant` (
`ass_id` smallint(5) UNSIGNED NOT NULL,
`ass_name` varchar(50) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `tblassistant` (`ass_id`, `ass_name`) VALUES
(1, 'Mary'),
(2, 'Andrew'),
(3, 'John'),
(4, 'Helen');
http://sqlfiddle.com/#!9/3fd588/3

SQL Get the total count of registered user in day by day

Can you help me to construct a query. The scenario is this, I want to get the total value of the registered users each day.
Select count(*)
from tableName
Where registered = true
Group by Date
Assuming that your registration timestamp is stored at created_at date-time field and table named say user:
SELECT created_at,COUNT(*) as `total registration` FROM `user` GROUP BY (DATE(`user`.`created_at`))
The former answers do neither cover the where part nor output the dayname.
This should do both and produce what you want:
create table #data (
reg_id int,
reg_email nvarchar(255),
reg_date datetimeoffset(7)
)
insert into #data(reg_id, reg_email, reg_date)
VALUES
(1, 'a', '2018-10-01'),
(2, 'b', '2018-10-01'),
(3, 'c', '2018-10-02'),
(4, 'd', '2018-10-03'),
(5, 'e', '2018-10-01'),
(6, 'f', '2018-10-02'),
(7, 'g', '2018-10-04'),
(8, 'h', '2018-10-05'),
(9, 'i', '2018-10-05'),
(10, 'j', '2018-10-06')
SELECT count(*), datename(dw, reg_date) from #data
where datepart(week, reg_date) = 40
group by reg_date
drop table #data
assuming that you are using sql server greater or equal 2008!
I finally get the correct data for today and last 7 days. Please refer below query.
SELECT
a.JourneyName,
a.BonusName,
a.Status,
a."Timestamp",
a.MID,
COUNT(CASE WHEN DATEDIFF(DD, a."Timestamp", GETDATE()) = 0 THEN 1 ELSE NULL END) as "Day_1",
COUNT(CASE WHEN DATEDIFF(DD, a."Timestamp", GETDATE()) = 1 THEN 1 ELSE NULL END) as "Day_2",
COUNT(CASE WHEN DATEDIFF(DD, a."Timestamp", GETDATE()) = 2 THEN 1 ELSE NULL END) as "Day_3",
COUNT(CASE WHEN DATEDIFF(DD, a."Timestamp", GETDATE()) = 3 THEN 1 ELSE NULL END) as "Day_4",
COUNT(CASE WHEN DATEDIFF(DD, a."Timestamp", GETDATE()) = 4 THEN 1 ELSE NULL END) as "Day_5",
COUNT(CASE WHEN DATEDIFF(DD, a."Timestamp", GETDATE()) = 5 THEN 1 ELSE NULL END) as "Day_6",
COUNT(CASE WHEN DATEDIFF(DD, a."Timestamp", GETDATE()) = 6 THEN 1 ELSE NULL END) as "Day_7"
FROM
TableName a
WHERE
a."Timestamp" >= DATEADD(DD, -7, GETDATE())
AND a."Timestamp" <= GETDATE()
GROUP BY``
a.JourneyName, a.BonusName, a.Status, a."Timestamp", a.MID
Total Count per day where Day_1 is today, Day_2 is yesterday and so on.

Count consecutive rows with a particular status

I need to count whether there are three consecutive failed login attempts of the user in last one hour.
For example
id userid status logindate
1 1 0 2014-08-28 10:00:00
2 1 1 2014-08-28 10:10:35
3 1 0 2014-08-28 10:30:00
4 1 0 2014-08-28 10:40:00
In the above example, status 0 means failed attempt and 1 means successful attempt.
I need a query that will count three consecutive records of a user with status 0 occurred in last one hour.
I tried below query
SELECT COUNT( * ) AS total, Temp.status
FROM (
SELECT a.status, MAX( a.id ) AS idlimit
FROM loginAttempts a
GROUP BY a.status
ORDER BY MAX( a.id ) DESC
) AS Temp
JOIN loginAttempts t ON Temp.idlimit < t.id
HAVING total >1
Result:
total status
2 1
I don't know why it display status as 1. I also need to add a where condition on logindate and status field but don't know how would it work
For consecutive count you can use user defined variables to note the series values ,like in below query i have use #g and #r variable, in inner query i am storing the current status value that could be 1/0 and in case expression i am comparing the value stored in #g with the status column if they both are equal like #g is holding previous row value and previous row's status is equal to the current row's status then do not change the value stored in #r,if these values don't match like #g <> a.status then increment #r with 1, one thing to note i am using order by with id column and assuming it is set to auto_increment so for consecutive 1s #r value will be same like #r was 3 for first status 1 and the again status is 1 so #r will 3 until the status changes to 0 same for status 0 vice versa
SELECT t.userid,t.consecutive,t.status,COUNT(1) consecutive_count
FROM (
SELECT a.* ,
#r:= CASE WHEN #g = a.status THEN #r ELSE #r + 1 END consecutive,
#g:= a.status g
FROM attempts a
CROSS JOIN (SELECT #g:=2, #r:=0) t1
WHERE a.`logindate` BETWEEN '2014-08-28 10:00:00' AND '2014-08-28 11:00:00'
ORDER BY id
) t
GROUP BY t.userid,t.consecutive,t.status
HAVING consecutive_count >= 3 AND t.status = 0
Now in parent query i am grouping results by userid the resultant value of case expression i have name is it as consecutive and status to get the count for each user's consecutive status
One thing to note for above query that its necessary to provide the
hour range like i have used between without this it will be more
difficult to find exactly 3 consecutive statuses with in an hour
Sample data
INSERT INTO attempts
(`id`, `userid`, `status`, `logindate`)
VALUES
(1, 1, 0, '2014-08-28 10:00:00'),
(2, 1, 1, '2014-08-28 10:10:35'),
(3, 1, 0, '2014-08-28 10:30:00'),
(4, 1, 0, '2014-08-28 10:40:00'),
(5, 1, 0, '2014-08-28 10:50:00'),
(6, 2, 0, '2014-08-28 10:00:00'),
(7, 2, 0, '2014-08-28 10:10:35'),
(8, 2, 0, '2014-08-28 10:30:00'),
(9, 2, 1, '2014-08-28 10:40:00'),
(10, 2, 1, '2014-08-28 10:50:00')
;
As you can see from id 3 to 5 you can see consecutive 0s for userid 1 and similarly id 6 to 8 userid 2 has consecutive 0s and they are in an hour range using above query you can have results as below
userid consecutive status consecutive_count
------ ----------- ------ -------------------
1 2 0 3
2 2 0 3
Fiddle Demo
M Khalid Junaid's answer is great, but his Fiddle Demo didn't work for me when I clicked it.
Here is a Fiddle Demo which works as of this writing.
In case it doesn't work later, I used the following in the schema:
CREATE TABLE attempts
(`id` int, `userid` int, `status` int, `logindate` datetime);
INSERT INTO attempts
(`id`, `userid`, `status`, `logindate`)
VALUES
(1, 1, 0, '2014-08-28 10:00:00'),
(2, 1, 1, '2014-08-28 10:10:35'),
(3, 1, 0, '2014-08-28 10:30:00'),
(4, 1, 0, '2014-08-28 10:40:00'),
(5, 1, 0, '2014-08-28 10:50:00'),
(6, 2, 0, '2014-08-28 10:00:00'),
(7, 2, 0, '2014-08-28 10:10:35'),
(8, 2, 0, '2014-08-28 10:30:00'),
(9, 2, 1, '2014-08-28 10:40:00'),
(10, 2, 1, '2014-08-28 10:50:00')
;
And this as the query:
SELECT t.userid,t.consecutive,t.status,COUNT(1) consecutive_count
FROM (
SELECT a.* ,
#r:= CASE WHEN #g = a.status THEN #r ELSE #r + 1 END consecutive,
#g:= a.status g
FROM attempts a
CROSS JOIN (SELECT #g:=2, #r:=0) t1
WHERE a.`logindate` BETWEEN '2014-08-28 10:00:00' AND '2014-08-28 11:00:00'
ORDER BY id
) t
GROUP BY t.userid,t.consecutive,t.status
HAVING consecutive_count >= 3 AND t.status = 0;

Subsequence in MySQL/CakePHP

In my mysql table I have a field which is a 4 letter Myers-Briggs personality type. I would like to search through the table and match when the personality type matches the one in the query by having 2 aspects in common. The way I understand this, it is really just finding the longest common subsequence of the two and testing that it is >= 2
Example:
'ISTJ' would match with 'INFJ', because the length of the common subsequence is 'IJ' >= 2
and
'ISTJ' would not match with 'INFP', because the length of the common subsequence is 'I' <= 2
Is there a way to do this in a mysql query? I am using CakePHP for the querying, so if you know how to do this with Cake that would also be helpful.
The Myer-Briggs personality types are positional. This means that you can compare character by character.
Here is one method, where you just have to put in the comparison string once:
select t.*
from (select t.*,
(case when substring(t.MyerBriggs, 1, 1) = substring(const.comp, 1, 1)
then 1 else 0
end) as MB1,
(case when substring(t.MyerBriggs, 2, 1) = substring(const.comp, 2, 1)
then 1 else 0
end) as MB2,
(case when substring(t.MyerBriggs, 3, 1) = substring(const.comp, 3, 1)
then 1 else 0
end) as MB3,
(case when substring(t.MyerBriggs, 4, 1) = substring(const.comp, 4, 1)
then 1 else 0
end) as MB4
from t cross join
(select 'INFJ' as comp) const
)
where (MB1+MB2+MB3+MB4) >= 2
You can actually simplify this in MySQL as:
select t.*
from t cross join
(select 'INFJ' as comp) const
where (if(substring(t.MyerBriggs, 1, 1) = substring(const.comp, 1, 1), 1, 0) +
if(substring(t.MyerBriggs, 2, 1) = substring(const.comp, 2, 1), 1, 0) +
if(substring(t.MyerBriggs, 3, 1) = substring(const.comp, 3, 1), 1, 0) +
if(substring(t.MyerBriggs, 4, 1) = substring(const.comp, 4, 1), 1, 0)
) >= 2
If I understand the Myers-Briggs thingy properly, there are two possibilities for each of the four categorisation axis, and the order of the letters is constant (and therefore carries no meaning).
In this case, you could use four two-state columns like the below, instead of one string:
CREATE TABLE profile (
user_id INT,
EI ENUM ('E', 'I'),
SN ENUM ('S', 'N'),
TF ENUM ('T', 'F'),
JP ENUM ('J', 'P')
);
Profile 'ISTJ' would be inserted like this:
INSERT INTO profile VALUE (1, 'I', 'S', 'T', 'J');
Matching with profile 'INFJ' would look like this:
SELECT * FROM profile WHERE
(EI = 'I') + (SN = 'N') + (TF = 'F') + (JP = 'J') >= 2

Group by, with rank and sum - not getting correct output

I'm trying to sum a column with rank function and group by month, my code is
select dbo.UpCase( REPLACE( p.Agent_name,'.',' '))as Agent_name, SUM(convert ( float ,
p.Amount))as amount,
RANK() over( order by SUM(convert ( float ,Amount )) desc ) as arank
from dbo.T_Client_Pc_Reg p
group by p.Agent_name ,p.Sale_status ,MONTH(Reg_date)
having [p].Sale_status='Activated'
Currently I'm getting all total value of that column not month wise
Name amount rank
a 100 1
b 80 2
c 50 3
for a amount 100 is total amount till now but , i want get current month total amount not last months..
Maybe you just need to add a WHERE clause? Here is a minor re-write that I think works generally better. Some setup in tempdb:
USE tempdb;
GO
CREATE TABLE dbo.T_Client_Pc_Reg
(
Agent_name VARCHAR(32),
Amount INT,
Sale_Status VARCHAR(32),
Reg_date DATETIME
);
INSERT dbo.T_Client_Pc_Reg
SELECT 'a', 50, 'Activated', GETDATE()
UNION ALL SELECT 'a', 50, 'Activated', GETDATE()
UNION ALL SELECT 'b', 20, 'Activated', GETDATE()
UNION ALL SELECT 'b', 20, 'Activated', GETDATE()
UNION ALL SELECT 'b', 20, 'Activated', GETDATE()
UNION ALL SELECT 'b', 20, 'Activated', GETDATE()
UNION ALL SELECT 'b', 20, 'NotActivated', GETDATE()
UNION ALL SELECT 'c', 25, 'Activated', GETDATE()
UNION ALL SELECT 'c', 25, 'Activated', GETDATE()
UNION ALL SELECT 'c', 25, 'Activated', GETDATE()-40;
Then the query:
SELECT
Agent_name = UPPER(REPLACE(Agent_name, '.', '')),
Amount = SUM(CONVERT(FLOAT, Amount)),
arank = RANK() OVER (ORDER BY SUM(CONVERT(FLOAT, Amount)) DESC)
FROM dbo.T_Client_Pc_Reg
WHERE Reg_date >= DATEADD(MONTH, DATEDIFF(MONTH, 0, CURRENT_TIMESTAMP), 0)
AND Reg_date < DATEADD(MONTH, DATEDIFF(MONTH, 0, CURRENT_TIMESTAMP) + 1, 0)
AND Sale_status = 'Activated'
GROUP BY UPPER(REPLACE(Agent_name, '.', ''))
ORDER BY arank;
Now cleanup:
USE tempdb;
GO
DROP TABLE dbo.T_Client_Pc_Reg;