MySQL GROUP BY WITH ROLLUP - want to ROLLUP all permutations - mysql

Take for example this table (let's call it BIN_TABLE):
+------+------+
| A | B |
+------+------+
| 0 | 0 |
| 0 | 1 |
| 1 | 1 |
| 1 | 0 |
+------+------+
I want to roll it up, so I do:
SELECT A, B, COUNT(*)
FROM BIN_TABLE
GROUP BY A, B WITH ROLLUP;
And I get:
+------+------+----------+
| A | B | COUNT(*) |
+------+------+----------+
| 0 | 0 | 1 |
| 0 | 1 | 1 |
| 0 | NULL | 2 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
| 1 | NULL | 2 |
| NULL | NULL | 4 |
+------+------+----------+
This is an example of how WITH ROLLUP uses the order of the fields I put in the GROUP BY clause.
I would like to also have the following lines in the result:
| NULL | 1 | 2 |
| NULL | 0 | 2 |
Which would mean that I have all of the rolled-up permutations.
Is this possible to do without resorting to this:
SELECT A, B, COUNT(*)
FROM BIN_TABLE
GROUP BY A, B WITH ROLLUP
UNION
SELECT NULL, B, COUNT(*)
FROM BIN_TABLE
GROUP BY B
(I use MySQL 5.6, if it matters)

No, I believe UNION is the only way. You could however use UNION ALL (rather than the implicit UNION DISTINCT) to save from needlessly searching for duplicates.

According the function of roll-up modifier, there are already all rolled-up permutations in your results.
Please look at these results.
+------+------+----------+
| A | B | COUNT(*) |
+------+------+----------+
| 0 | 0 | 1 |
| 0 | 1 | 1 |
| 0 | NULL | 2 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
| 1 | NULL | 2 |
| NULL | NULL | 4 |
+------+------+----------+
Here, 3rd record and 6th record are the rolled-up permutations.
So we can get the result by a following sql.
SELECT * from
(select a,b,count(*) from
bin_table group by a,b with rollup) total
order by total.b desc,a;
+------+------+----------+
| a | b | count(*) |
+------+------+----------+
| 0 | 1 | 1 |
| 1 | 1 | 1 |
| 0 | 0 | 1 |
| 1 | 0 | 1 |
| NULL | NULL | 4 |
| 0 | NULL | 2 |
| 1 | NULL | 2 |
+------+------+----------+
7 rows in set (0.01 sec)
Here, 6th record and 7th record are the rolled-up permutations.
And I think that it will make the performance better.
Thanks,

Related

MYSQL group by for separate id

So here is the structure and query: http://www.sqlfiddle.com/#!9/de7ec8/4
And this is the result:
+-------------+--------+
| id_requests | status |
+-------------+--------+
| 1 | 0 |
| 1 | 0 |
| 1 | 1 |
| 2 | 1 |
+-------------+--------+
4 rows in set
and im looking for this result:
+-------------+--------+
| id_requests | status |
+-------------+--------+
| 1 | 0 |
| 1 | 1 |
| 2 | 1 |
+-------------+--------+
3 rows in set
I want to group by status but for each id_requests, any help will be appreciated.
Seems you are looking for selecting distinct rows for this use distinct clause
SELECT distinct a.id_requests, b.status
FROM `admin_availability_requests` a

SQL Distinct a column with conditions

I'm working on a query where I need to count distinct CarId row when the column LocationId is not null and get all CarId if its null or 0 but the query that I tried distincts all the CarId even if its null
#LocId int
Select Count(distinct a.CarId) from VehicleDetails a
inner join VehicleDocuments b on a.DocId=b.DocId
left join VehicleShipmentDetails dpg on dpg.VehicleShipmentId= b.VehicleShipmentId
where b.LogicalDelete=0 and a.LogicalDelete=0
and (dpg.LocationId= #LocId or dpg.LocationId= 0 or dpg.LocationId is null)
| ID | CarId | LocationId | DateCreated |
|------+----------------+-----------------+---------------|
| 1 | 1 | 5 | 02/03/2019 |
| 2 | 2 | null | 01/14/2019 |
| 3 | 2 | 0 | 02/03/2019 |
| 4 | 2 | 5 | 12/30/2018 |
| 5 | 4 | 3 | 01/10/2019 |
| 6 | 3 | 5 | 02/14/2019 |
| 7 | 2 | 5 | 03/13/2019 |
Desired output:
| ID | CarId | LocationId | DateCreated |
+------+----------------+-----------------+---------------+
| 1 | 1 | 5 | 02/03/2019 |
| 2 | 2 | null | 01/14/2019 |
| 3 | 2 | 0 | 02/03/2019 |
| 4 | 2 | 5 | 03/13/2019 |
| 5 | 4 | 3 | 01/10/2019 |
| 6 | 3 | 5 | 02/14/2019 |
Current Output
| ID | CarId | LocationId | DateCreated |
+------+----------------+-----------------+---------------+
| 1 | 1 | 5 | 02/03/2019 |
| 2 | 2 | 5 | 01/14/2019 |
| 3 | 4 | 3 | 01/10/2019 |
| 4 | 3 | 5 | 02/14/2019 |
Im getting a count of 4 but i needed to have 6 as the Count
EDIT: My goal is to remove the row to Distinct CarId if the value of the LocationId is Null or 0 but on my Current code, It distincts all CarId that is null,0 and equals to #LocId
You can query something like this, replace your_table by your actual set of data.
SELECT ID, CardId, LocationId, DateCreated
FROM your_table as T
WHERE NOT EXISTS (SELECT *
FROM your_table as T1
WHERE T.ID > T1.ID AND T.CarID = T1.CarID)
In SQL, you can use the statement CASE to manage conditions (just like the "if then else" in other programming languages). In your case this function could help because you have two differents cases to handle.

Similar SQLs with session variable produce different result

MariaDB 10.1.18
Table P (id int AUTO_INCREMENT, rownum int, code int, s int, PRIMARY KEY(id));
select id, rownum, code, s from P order by id;
+----+--------+------+------+
| id | rownum | code | s |
+----+--------+------+------+
| 1 | 1 | 5 | 1 |
| 2 | 2 | 5 | 1 |
| 3 | 3 | 5 | 1 |
| 4 | 4 | 5 | 1 |
| 5 | 5 | 5 | 1 |
| 6 | 6 | 7 | 1 |
| 7 | 7 | 7 | 1 |
| 8 | 8 | 7 | 1 |
| 9 | 9 | 7 | 1 |
| 10 | 10 | 7 | 1 |
+----+--------+------+------+
Issue: the following 2 queries are very similar: 1st query join on id, 2nd join on rownum; the id and rownum columns are having the same values (see table above), but the query result is different in calculated column N:
Query 1: join on id column
SELECT P.id, P.rownum, P2.s,
IF(P2.s IS NULL, #val:=#val+1, #val) as N
FROM P CROSS JOIN (SELECT #val:=0) init
LEFT JOIN P P2
ON (P.id+1=P2.id AND P.s=1 AND P2.s=1 AND P.code = P2.code)
ORDER BY P.id;
+----+--------+------+------+
| id | rownum | s | N |
+----+--------+------+------+
| 1 | 1 | 1 | 0 |
| 2 | 2 | 1 | 0 |
| 3 | 3 | 1 | 0 |
| 4 | 4 | 1 | 0 |
| 5 | 5 | NULL | 1 |
| 6 | 6 | 1 | 1 |
| 7 | 7 | 1 | 1 |
| 8 | 8 | 1 | 1 |
| 9 | 9 | 1 | 1 |
| 10 | 10 | NULL | 2 |
+----+--------+------+------+
Query 2: join on rownum column
SELECT P.id, P.rownum, P2.s,
IF(P2.s IS NULL, #val:=#val+1, #val) as N
FROM P CROSS JOIN (SELECT #val:=0) init
LEFT JOIN P P2
ON (P.rownum+1=P2.rownum AND P.s=1 AND P2.s=1 AND P.code = P2.code)
ORDER BY P.id;
+----+--------+------+------+
| id | rownum | s | N |
+----+--------+------+------+
| 1 | 1 | 1 | 0 |
| 2 | 2 | 1 | 0 |
| 3 | 3 | 1 | 0 |
| 4 | 4 | 1 | 0 |
| 5 | 5 | NULL | 1 |
| 6 | 6 | 1 | 0 |
| 7 | 7 | 1 | 0 |
| 8 | 8 | 1 | 0 |
| 9 | 9 | 1 | 0 |
| 10 | 10 | NULL | 2 |
+----+--------+------+------+
As explicitly documented both in MariaDB knowledge base and MySQL manual, you should not read a user-defined variable and set its value in the same statement, unless this statement is SET. For other statements it is unsafe and the result is not guaranteed, as your example clearly demonstrates.
Additional note: variables you are talking about in your question are more commonly referred to as 'user-defined variables' or 'user variables', as opposed to 'system variables', 'global variables' and 'session variables', each of which imply system-defined variables, either all of them or limited to the given scope.

SQL - filter duplicate rows based on a value in a different column

I have an SQL table:
+-------------+-----------+---------+
| ID | position | user |
+-------------+-----------+---------+
| 1 | 1 | 0 |
| 2 | 2 | 0 |
| 3 | 3 | 0 |
| 4 | 4 | 0 |
| 5 | 5 | 0 |
| 6 | 6 | 0 |
| 7 | 7 | 0 |
| 8 | 7 | 1 |
+-------------+-----------+---------+
I would like to filter the duplicate row based on position column and the distinct value of user column, for the first query I need to have the following result:
+-------------+-----------+---------+
| ID | position | user |
+-------------+-----------+---------+
| 1 | 1 | 0 |
| 2 | 2 | 0 |
| 3 | 3 | 0 |
| 4 | 4 | 0 |
| 5 | 5 | 0 |
| 6 | 6 | 0 |
| 8 | 7 | 1 |
+-------------+-----------+---------+
For the second query I need the following:
+-------------+-----------+---------+
| ID | position | user |
+-------------+-----------+---------+
| 1 | 1 | 0 |
| 2 | 2 | 0 |
| 3 | 3 | 0 |
| 4 | 4 | 0 |
| 5 | 5 | 0 |
| 6 | 6 | 0 |
| 7 | 7 | 0 |
+-------------+-----------+---------+
What queries do I need to achieve this?
Thanks.
In the absence of further information, the two queries below assume that you want to resolve duplicate positions by taking either the larger (maximum) user value, in the first case, or the smaller (minimum) user value in the second case.
First query:
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT position, MAX(user) AS max_user
FROM yourTable
GROUP BY position
) t2
ON t1.position = t2.position AND
t1.user = t2.max_user
Second query:
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT position, MIN(user) AS min_user
FROM yourTable
GROUP BY position
) t2
ON t1.position = t2.position AND
t1.user = t2.min_user

SQL Running Percentage

I have the following data in an MySQL database:
User_ID | Film | Choice
Where choice is either 1 or 0. I would like to create a google chart with the percentage choice (1s) after each film. I can produce this in excel as below:
Is there a way to produce a result which has these running totals, without creating a new column in the table? (similar to SUM)
First row should be 1 but set to 0 just to make the graph look better!
Assuming #fancyPants is right (ususally is), then you can also write that this way (slightly simpler and less scalable)...
SELECT x.*
, SUM(y.choice)/COUNT(*) * 100 pct
FROM film_choice x
JOIN film_choice y
ON y.film <= x.film
GROUP
BY x.film;
The first row should be 100%, right? 1 vote, 1 yes equals 100%
create table runningp(id int auto_increment primary key, choice tinyint);
insert into runningp(choice) values (1),(0),(0),(1),(1),(0),(1),(1),(0),(0);
select r.*, #p:=#p+choice, #t:=#t+1, #p/#t as yes
from runningp r,
(select #p:=0, #t:=0) v
order by id;
+----+--------+---------------+----------+--------+
| id | choice | #p:=#p+choice | #t:=#t+1 | yes |
+----+--------+---------------+----------+--------+
| 1 | 1 | 1 | 1 | 1.0000 |
| 2 | 0 | 1 | 2 | 0.5000 |
| 3 | 0 | 1 | 3 | 0.3333 |
| 4 | 1 | 2 | 4 | 0.5000 |
| 5 | 1 | 3 | 5 | 0.6000 |
| 6 | 0 | 3 | 6 | 0.5000 |
| 7 | 1 | 4 | 7 | 0.5714 |
| 8 | 1 | 5 | 8 | 0.6250 |
| 9 | 0 | 5 | 9 | 0.5556 |
| 10 | 0 | 5 | 10 | 0.5000 |
+----+--------+---------------+----------+--------+
To just select what you need:
select id, choice, yes from (
select r.*, #p:=#p+choice, #t:=#t+1, #p/#t as yes
from runningp r,
(select #p:=0, #t:=0) v
order by id
) sq
order by id;
+----+--------+--------+
| id | choice | yes |
+----+--------+--------+
| 1 | 1 | 1.0000 |
| 2 | 0 | 0.5000 |
| 3 | 0 | 0.3333 |
| 4 | 1 | 0.5000 |
| 5 | 1 | 0.6000 |
| 6 | 0 | 0.5000 |
| 7 | 1 | 0.5714 |
| 8 | 1 | 0.6250 |
| 9 | 0 | 0.5556 |
| 10 | 0 | 0.5000 |
+----+--------+--------+
10 rows in set (0.00 sec)