mysql - possible to pivot across a dynamic date column? - mysql

consider the table
date
id
9/2
1
9/2
2
9/2
3
9/3
4
this can be pivoted up using count and case statements for each date
9/2
9/3
3
1
but is it possible to do the same thing without knowledge of the dates present in date column?

Yes, it is possible using the GROUP BY clause. As the clause name indicates, you can group the rows by a certain column or set of columns. You can combine that with the COUNT function to achieve your goal.
SELECT date, COUNT(*)
FROM TableName
GROUP BY date

If you are using MySQL v8 that supports recursive common table expression (cte), then you can combine that with prepared statement. I assume that the sample data you're provided in the question does not represent the real data you have especially the date format so let's say that your table is like this:
id
date
1
2022-09-02
2
2022-09-02
3
2022-09-02
4
2022-09-03
Whereby the date value is following the standard MySQL date format, here's a CREATE TABLE syntax:
CREATE TABLE testtable (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
date DATE);
Now, it seems like you know how to pivot the result using query so I assume it would look something like this:
SELECT SUM(CASE WHEN date="2022-09-02" THEN 1 ELSE 0 END) AS "2022-09-02",
SUM(CASE WHEN date="2022-09-03" THEN 1 ELSE 0 END) AS "2022-09-03"
FROM testtable;
If your query is nothing like the above then establish your own final structure of the query before you start. This will help you in shaping the query for the prepared statement.
The first step I'm doing here is generating the dynamic columns based on the date value. I'm using RECURSIVE cte here with the assumption that even skipped dates will still be shown as zeros:
WITH RECURSIVE cte AS (
SELECT MIN(date) AS mindt, MAX(date) AS maxdt
FROM testtable
UNION
SELECT mindt + INTERVAL 1 DAY, maxdt
FROM cte
WHERE mindt + INTERVAL 1 DAY <= maxdt)
SELECT * FROM cte
Based on your sample data, the result will return like this:
mindt
maxdt
2022-09-02
2022-09-16
2022-09-03
2022-09-16
The column that we will use for the rest of the process is just mindt. Column maxdt here is just for the cte reference on when to stop generating the running date.
Note: If you don't wish to show skipped dates then you don't need to use RECURSIVE cte and your final query should able to use on older MySQL versions as well. I'll include that option later in this answer.
Once we have that, we can start building our query for the prepared statement. Let's generate SUM(CASE WHEN date="2022-09-02" THEN 1 ELSE 0 END) AS "2022-09-02" (refer the final query above) from the RECURSIVE cte statement, so:
.....
SELECT CONCAT('SUM(CASE WHEN date="',mindt,'" THEN 1 ELSE 0 END) AS "',mindt,'"')
FROM cte
This is what you'll get from that:
CONCAT('SUM(CASE WHEN date="',mindt,'" THEN 1 ELSE 0 END) AS "',mindt,'"')
SUM(CASE WHEN date="2022-09-02" THEN 1 ELSE 0 END) AS "2022-09-02"
SUM(CASE WHEN date="2022-09-03" THEN 1 ELSE 0 END) AS "2022-09-03"
We'll do a GROUP_CONCAT() statement to make those results returned as a single row:
SELECT GROUP_CONCAT(CONCAT('SUM(CASE WHEN date="',mindt,'" THEN 1 ELSE 0 END) AS "',mindt,'"'))
FROM cte
This is what you'll get:
GROUP_CONCAT(CONCAT('SUM(CASE WHEN date="',mindt,'" THEN 1 ELSE 0 END) AS "',mindt,'"'))
SUM(CASE WHEN date="2022-09-02" THEN 1 ELSE 0 END) AS "2022-09-02",SUM(CASE WHEN date="2022-09-03" THEN 1 ELSE 0 END) AS "2022-09-03"
Now, the result above looks like the columns I'm aiming for the final query. I'll add another CONCAT() to add SELECT and FROM table_name like so:
...
SELECT CONCAT('SELECT ',
GROUP_CONCAT(
CONCAT('SUM(CASE WHEN date="',mindt,'" THEN 1 ELSE 0 END) AS "',mindt,'"')),
' FROM testtable;')
FROM cte;
This will return result of a query exactly the same as the one I'm aiming for my final query above. What I'll do next is just to insert the result of that into a variable and do the prepared statement operation. Let's say #sql is my variable:
SET #sql := NULL; /*setting as NULL just in case the variable is not empty*/
WITH RECURSIVE cte AS (
SELECT MIN(date) AS mindt, MAX(date) AS maxdt
FROM testtable
UNION
SELECT mindt + INTERVAL 1 DAY, maxdt
FROM cte
WHERE mindt + INTERVAL 1 DAY <= maxdt)
SELECT CONCAT('SELECT ',
GROUP_CONCAT(
CONCAT('SUM(CASE WHEN date="',mindt,'" THEN 1 ELSE 0 END) AS "',mindt,'"')),
' FROM testtable;') INTO #sql
FROM cte;
/*prepare, execute then deallocate the statement generated from #sql variable*/
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
The good thing about this is, if you're just adding data into the table and conditions are still the same - to pivot and count occurrence by date - you don't need to change the query at all.
Check this fiddle for more examples and explanation including the option to exclude skipped dates in the result.

Related

Summing of count result at same level

I'm trying to sum the results of count(id) at the same level, in order to find out the relative portion of the count(id) from the overall count.
The count is grouped by the respective previous number, and I want to stay at the same table and have it all together.
`
select totalattempts, count(totalattempts) allattempts, count(case when success>0 then totalattempts else null end) successfulattempts
from (
select *, case when success> 0 then attemptspresuccess+1 else attemptspresuccess end totalattempts
from (select orderid, count(orderid) attemptspresuccess, count(case when recoveredPaymentId is not null then recoveredPaymentId end ) success from (
select orderid, recoveredPaymentId
from errors
where platform = 'woo'
) alitable
group by orderid) minitable ) finaltable
group by totalattempts
order by totalattempts asc
`
I need to add another column that basically would have, to put it simply, count(totalattempts)/sum(count(totalattempts).
I'm running out of ideas basically.
I can't use windows as this is an app of retool which doesn't support that
Assuming some test data here:
DECLARE #table TABLE (AttemptNumber INT IDENTITY, Success BIT)
INSERT INTO #table (Success) VALUES
(0),(0),(0),(0),(1),(1),(0),(0),(0),(0),(0),(1),(0),(1),(0),(0),
(0),(0),(1),(0),(0),(0),(0),(1),(0),(1),(0),(0),(0),(1),(0),(0)
I sounds like you want to know how many attempts there were, how many were successful and what that is a percentage?
SELECT COUNT(Success) AS TotalCount,
COUNT(CASE WHEN Success = 1 THEN 1 END) AS SuccessCount,
COUNT(CASE WHEN Success = 1.0 THEN 1 END)/(COUNT(Success)+.0) AS SuccessPct
FROM #table
TotalCount SuccessCount SuccessPct
--------------------------------------
32 8 0.2500000000000

SQL query to get percentages within a grouping

I've looked over similar questions and I just can't seem to get this right.
I have a table with three columns: ID, Date, and Method. None are unique.
I want to be able to see for any given date, how many rows match a certain pattern on Method.
So, for example, if the table has 100 rows, and 8 of them have the date "01-01-2020" and of those 8, two of them have a method of "A", I would want a return row that says "01-01-2020", "8", "2", and "25%".
My SQL is pretty rudimentary. I have been able to make a query to get me the count of each method by date:
select Date, count(*) from mytable WHERE Method="A" group by Date;
But I haven't been able to figure out how to put together the results that I am needing. Can someone help me out?
You could perform a count over a case expression for that method, and then divide the two counts:
SELECT date,
COUNT(*),
COUNT(CASE method WHEN 'A' THEN 1 END),
COUNT(CASE method WHEN 'A' THEN 1 END) / COUNT(*) * 100
FROM mytable
GROUP BY date
I'm assuming you're interested in all methods rather than just 'A', so you could do the following:
with ptotals as
(
SELECT
thedate,
count(*) as NumRows
FROM
mytable
group by
thedate
)
select
mytable.thedate,
mytable.themethod,
count(*) as method_count,
100 * count(*) / max(ptotals.NumRows) as Pct
from
mytable
inner join
ptotals
on
mytable.thedate = ptotals.thedate
group by
mytable.thedate,
mytable.themethod
You can use AVG() for the ratio/percentage:
SELECT date, COUNT(*),
SUM(CASE WHEN method = 'A' THEN 1 ELSE 0 END),
AVG(CASE WHEN method = 'A' THEN 100.0 ELSE 0 END)
FROM t
GROUP BY date;

req_type wise count id in one query

I have an sql query problem . I don't want to execute three times query for same result.
In my table I have one field req_type which have three parameter ,
either 1, either 2, either 3 .
I want counter based on req_type in one query instead of by executing query 3 times like below
select count(id) as premium FROM tablename where req_type=1
select count(id) as premium1 FROm tablename where req_type=2
select count(id) as premium2 FROm tablename where req_type=3
I am stuck , can anybody help me?
You could use case for such type of count
select sum(case when req_type=1 then 1 else 0 end) as premium,
sum(case when req_type=2 then 1 else 0 end) as premium1,
sum(case when req_type=3 then 1 else 0 end) as premium2
FROM tablename
Use one query instead of threes by using group by cluase
select req_type , count(id) as premium
FROM tablename
where req_type in (1,2,3)
group by req_type
Use a GROUP BY
SELECT req_type, COUNT(id) AS count_premium
FROM tablename
GROUP BY req_type;

Query to display the % of the failure rate

I have written this query to get my data, and all the data is fine.
I have one column which has either Pass Or Fail. I want to calculate the % of number of bookings that failed, and output it in a single value.
I will have to write another query to show that one number.
Perhaps this? You didn't really offer much to go on:
SELECT
SUM(CASE
WHEN trip_rating.rating <= 3 OR
TIMESTAMPDIFF(MINUTE, booking_activity.activity_time, booking.pick_up_time) < 0
THEN 1.00 ELSE NULL END
) / COUNT(*)
FROM ...
Query
select
concat(
cast(
(
sum(case when t.Decision = 'Fail' then 1 else 0 end) / count(*)) * 100
as varchar(3)
), '%') as `fail rate`
from
(
select ........
case ........ end as Decision
)t;

SQL Union query error

Im trying to join two count querys
SELECT COUNT(*) AS total FROM clients WHERE addedby = 1
UNION
SELECT COUNT(*) AS converts FROM clients WHERE addedby = 1 AND status = '6'
What this returns is
total
4
0
this is the correct data, what I was expecting was this
total converts
4 0
You don't need a UNION query to do this. SELECT A UNION SELECT B returns the rows of A followed by the rows of B (deduplicated; if you want all rows from both datasets, use UNION ALL).
What you want is something like this:
select
(select count(*) from clients where addedby=1) as total,
(select count(*) from clients where addedby=1 and status='6') as converts
Other way to do this is using a case ... end expression that returns 1 if status='6':
select
count(*) from clients,
sum(case when status='6' then 1 else 0 end) as converts
from clients
No UNION needed, do it in one pass.
SELECT COUNT(*) as total,
SUM(CASE status WHEN '6' THEN 1 ELSE 0 END) as converts
FROM clients;
The simplest way to write this query is as a conditional aggregation:
select count(*) as total, sum(status = '6') as converts
from cleints
where addedby = 1;
MySQL treats booleans as integers with 1 being true and 0 being false. You can just sum of the values to get a count.