Here is my example (mysql Ver 14.14 Distrib 5.7.21, for Win64 (x86_64))
DROP TABLE IF EXISTS t_tt;
CREATE TEMPORARY TABLE t_tt SELECT 1 AS tid,'team 1' AS teamName,111 AS teamData;
INSERT INTO t_tt VALUES(2,'team 2',222);
INSERT INTO t_tt VALUES(3,'team 3',333);
SELECT
tid,
isnull(tid),
IF(isnull(tid),'total',teamName) AS displayName,
SUM(teamData)
FROM t_tt GROUP BY tid WITH ROLLUP;
And I think the result must be :
+-----+-------------+-------------+---------------+
| tid | isnull(tid) | displayName | SUM(teamData) |
+-----+-------------+-------------+---------------+
| 1 | 0 | team 1 | 111 |
| 2 | 0 | team 2 | 222 |
| 3 | 0 | team 3 | 333 |
| 3 | 1 | total | 666 |
+-----+-------------+-------------+---------------+
Actually,the real answer is :
+-----+-------------+-------------+---------------+
| tid | isnull(tid) | displayName | SUM(teamData) |
+-----+-------------+-------------+---------------+
| 1 | 0 | team 2 | 111 |
| 2 | 0 | team 3 | 222 |
| 3 | 0 | team 3 | 333 |
| NULL| 0 | team 3 | 666 |
+-----+-------------+-------------+---------------+
I dont know why the column of displayName is begins with "team 2" ,but not "team 1".
And the last row isnull(tid) should equals 1 , but not 0.
The problem is that you are using a MySQL extension. You have teamName in the SELECT, but it is not in the GROUP BY. So, MySQL is free to take this value from any row. To be honest, I thought it would need to be from a matching row. I don't fully understand the behavior.
But, this is easy to fix. One simple method is simply to add an aggregation function:
SELECT tid, tid is null,
COALESCE(max(teamName), 'total') AS displayName,
SUM(teamData)
FROM t_tt
GROUP BY tid WITH ROLLUP;
Note that for the additional row from the ROLLUP, the value is arbitrary.
I also found that the results were fixed if the table were pre-declared with a primary key:
CREATE TABLE t_tt (
tid int primary key,
teamName varchar(255),
teamData int
);
INSERT INTO t_tt VALUES(1, 'team 1', 111);
INSERT INTO t_tt VALUES(2, 'team 2', 222);
INSERT INTO t_tt VALUES(3, 'team 3', 333);
It's better to process all the extra information out of the group query, since some columns not grouped could have an arbitrary value:
SELECT tid,
isnull(tid),
IF(isnull(tid),'total',teamName) AS displayName,
team_sum
FROM
(
SELECT
tid,
teamName,
SUM(teamData) as team_sum
FROM t_tt
GROUP BY tid WITH ROLLUP
) t
See this Sql Fiddle: http://sqlfiddle.com/#!9/8871cc/9
Related
im new in sql. I cannot get data with format what i want in one step. Now i'm using more sql commands. I want to get all data in one command because i cant to connect them in subquery with group by. Somebodys can help me?
example of Table i have:
id
order_id
order_status
1
1
0
2
1
0
3
1
0
4
1
1
5
1
1
6
2
0
7
2
0
8
2
1
Table i want to have after sql query:
order_id
count
of
progress(%)
1
2
5
40
2
1
3
33
queries i use:
SELECT order_id, COUNT(status) as count
FROM `orders`
WHERE status = 1
GROUP by order_id;
SELECT order_id, COUNT(status) as of
FROM `orders`
GROUP by order_id;
SELECT order_id,
CAST((SELECT COUNT(status) FROM `orders` WHERE status = 1) /
(SELECT COUNT(status) FROM `orders`) *100 as int) AS progress FROM orders
group by order_id;
but last working properly only if i use where to single order id.
I want to make this data in one sql query to format i showed up.
Thanks a lot guys!
You don't need subqueries to do this, SQL's ordinary aggregate functions already work as you want with your group by clause:
SELECT order_id,
SUM(order_status) AS `count`,
COUNT(*) AS `of`,
SUM(order_status) / COUNT(order_status) * 100 as `progress`
FROM orders
group by order_id;
See example at http://sqlfiddle.com/#!9/d1799db/4/0
you need to use multiple subqueries
here's a query that I used and worked on your example on the onecompiler.com website
-- create
CREATE TABLE EMPLOYEE (
order_id INTEGER,
order_status INTEGER
);
-- insert
INSERT INTO EMPLOYEE VALUES (1,0 );
INSERT INTO EMPLOYEE VALUES (1, 0);
INSERT INTO EMPLOYEE VALUES (1, 0);
INSERT INTO EMPLOYEE VALUES (1, 1);
INSERT INTO EMPLOYEE VALUES (1,1 );
INSERT INTO EMPLOYEE VALUES (2, 0);
INSERT INTO EMPLOYEE VALUES (2, 0);
INSERT INTO EMPLOYEE VALUES (2, 1);
select *
from EMPLOYEE;
SELECT order_id, count, off , count/off
from(
select distinct order_id as order_id,
(select count(order_id) from EMPLOYEE C WHERE A.order_id=C.order_id AND order_status =1) as 'count',
(select count(order_id) from EMPLOYEE B WHERE A.order_id=B.order_id ) as 'off'
FROM EMPLOYEE A
) AA
;
You need to use sum and count with group by.
create table orders(
id int,
order_id int,
order_status int);
insert into orders values
(1,1,0),
(2,1,0),
(3,1,0),
(4,1,1),
(5,1,1),
(6,2,0),
(7,2,0),
(8,2,1);
select
order_id,
sum(order_status) count,
count(order_id) "of",
(100 * sum(order_status))
/ count(order_id) progress
from orders
group by order_id
order by order_id;
order_id | count | of | progress
-------: | ----: | -: | -------:
1 | 2 | 5 | 40.0000
2 | 1 | 3 | 33.3333
db<>fiddle here
i was described my problem without some details, w i want to join with other table but i see only record with status
oders_details
| id | order_describe | order_date |
|:----:|:--------------:|:----------:|
| 1 | sample 1 | 2022-02-28 |
| 2 | sample 2 | 2022-02-28 |
| 3 | sample 3 | 2022-03-01 |
| 4 | sample 4 | 2022-03-02 |
orders_status
| id | order_id |order_status|
|:---:|:---------------:|:----------:|
| 1 | 1 | 0 |
| 2 | 1 | 0 |
| 3 | 1 | 0 |
| 4 | 1 | 1 |
| 5 | 1 | 1 |
| 6 | 2 | 0 |
| 7 | 2 | 0 |
| 8 | 2 | 1 |
table i want after query
orders_view
| id |order_id|order_describe| order_date | count | of | progress |
|-----|--------|--------------|------------|-------|----|:--------:|
| 1 | 1 | sample 1 | 2022-02-28| 2 | 5 | 40 |
| 2 | 2 | sample 2 | 2022-02-28| 1 | 3 | 33 |
| 3 | 3 | sample 3 | 2022-03-01| null |null| null |
| 4 | 4 | sample 4 | 2022-03-02| null |null| null |
i want to get some hint what i have todo, to get finally table or view, not complete solution, to better understand sql lang
I'm validating following SQL approach from the community where I'm completely updating the user's roles. (Full update of the join table)
User
+----+-------+------+
| id | first | last |
+----+-------+------+
| 1 | John | Doe |
| 2 | Jane | Doe |
+----+-------+------+
Role
+----+----------+
| id | name |
+----+----------+
| 1 | admin |
| 2 | accounts |
| 3 | sales |
+----+----------+
UserRole
+--------+--------+
| userid | roleid |
+--------+--------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 2 |
| 2 | 3 |
+--------+--------+
My SQL approach -> first delete all, second insert all records
DELETE FROM UserRole WHERE userid = 1;
INSERT INTO UserRole(userid, roleid) VALUES(1, 2), (1, 3);
Is there a better way? I mean to do this in a single query possibly for these sorts of linking/join tables?
Edit
I think what I should have said to find an efficient SQL operation instead of a single query.
Here's another SQL
DELETE FROM UserRole WHERE user_id = 1 AND role_id NOT IN (2, 3);
INSERT INTO UserRole(user_id, role_id) VALUES(1, 2), (1, 3)
ON DUPLICATE KEY UPDATE user_id = VALUES(user_id), role_id = VALUES(role_id);
If you want to add all roles for all users into the userRoles table, you can delete from userRoles, then recreate table like below:
DECLARE #users TABLE ( userID INT, UserName NVARCHAR(MAX) )
DECLARE #roles TABLE ( roleID INT, RoleName NVARCHAR(MAX) )
DECLARE #userRoles TABLE ( userID INT, roleID INT )
INSERT INTO #users (userID,UserName) VALUES (1,'name1'),(2,'name2'),(3,'name3')
INSERT INTO #roles (roleID,RoleName) VALUES (1,'admin'),(2,'accounts'),(3,'sales')
INSERT INTO #userRoles (userID,roleID)
SELECT U.userID,R.roleID FROM #users U
FULL OUTER JOIN #roles R ON 1=1
ORDER BY userID
SELECT * FROM #userRoles
OUTPUT:
userID roleID
1 1
2 1
3 1
1 2
2 2
3 2
1 3
2 3
3 3
I have two tables like this:
person:
id | name | sale | commission
1 | abc | 0 | 0
2 | xyz | 0 | 0
sale:
id | date | person_id | sale | commission
1 | 2016-05-01 | 1 | 10 | 1
2 | 2016-05-02 | 1 | 10 | 1
3 | 2016-05-03 | 1 | 10 | 1
4 | 2016-05-01 | 2 | 20 | 2
5 | 2016-05-02 | 2 | 20 | 2
6 | 2016-05-01 | 2 | 20 | 2
I want to update person table with single update query and change the table something like this:
person:
id | name | sale | commission
1 | abc | 30 | 3
2 | xyz | 60 | 6
I know I can sum sale like following but how to update following query result into person table directly.
SELECT person_id, SUM(sale), SUM(commission)
FROM sale
GROUP BY person_id;
As Strawberry said in the comments under your question, think long and hard before you save this information. It is denormalized, and it becomes stale. Rather, consider using it during report generation. Otherwise, well, as said, you may run into problems.
drop table if exists person;
create table person
( personId int auto_increment primary key,
name varchar(100) not null,
totSales decimal(9,2) not null,
totComm decimal(9,2)
);
insert person(name,totSales,totComm) values
('Joe',0,0),
('Sally',0,0);
-- just added persons 1 and 2 (auto_inc)
drop table if exists sale;
create table sale
( saleId int auto_increment primary key,
saleDate date not null,
personId int not null,
sale decimal(9,2) not null,
commission decimal(9,2) not null,
index(personId), -- facilitate a snappier "group by" later
foreign key (personId) references person(personId) -- Ref Integrity
);
insert sale(saleDate,personId,sale,commission) values
('2016-05-01',2,10,1),
('2016-05-01',1,40,4),
('2016-05-02',1,30,3),
('2016-05-07',2,10,1),
('2016-05-07',2,90,9);
-- the following dies on referential integrity, FK, error 1452 as expected
insert sale(saleDate,personId,sale,commission) values ('2016-05-01',4,10,1);
The update statement
update person p
join
( select personId,sum(sale) totSales, sum(commission) totComm
from sale
group by personId
) xDerived
on xDerived.personId=p.personId
set p.totSales=xDerived.totSales,p.totComm=xDerived.totComm;
The results
select * from person;
+----------+-------+----------+---------+
| personId | name | totSales | totComm |
+----------+-------+----------+---------+
| 1 | Joe | 70.00 | 7.00 |
| 2 | Sally | 110.00 | 11.00 |
+----------+-------+----------+---------+
2 rows in set (0.00 sec)
xDerived is merely an alias name. All derived tables need an alias name, whether or not you use the alias name explicitly.
UPDATE person
SET sale = (
SELECT SUM(s.sale) FROM sale s
WHERE s.person_id = person.id
);
works for me. See it in action at: http://ideone.com/F32oUU
EDIT for new version with additional aggregated column:
UPDATE person SET
sale = (
SELECT SUM(s.sale) FROM sale s
WHERE s.person_id = person.id
),
commission = (
SELECT SUM(s.commission) FROM sale s
WHERE s.person_id = person.id
);
http://ideone.com/yo1A9Y
This being said, I feel sure that a JOIN solution is better, and am hopeful another answerer will be able to post such a solution.
I have the following schema (mysql)
create table test(
userid int(11) not null,
item varchar(15),
bookid int(11));
insert into test values ('1','journal',NULL);
insert into test values ('1','journal',NULL);
insert into test values ('1','book',NULL);
insert into test values ('2','book',NULL);
insert into test values ('2','journal',NULL);
insert into test values ('1','book',NULL);
insert into test values ('2','journal',NULL);
insert into test values ('3','book',NULL);
insert into test values ('1','book',NULL);
insert into test values ('1','journal',NULL);
insert into test values ('3','journal',NULL);
insert into test values ('1','journal',NULL);
insert into test values ('2','journal',NULL);
insert into test values ('2','book',NULL);
insert into test values ('2','journal',NULL);
insert into test values ('1','journal',NULL);
insert into test values ('3','book',NULL);
insert into test values ('3','book',NULL);
insert into test values ('3','book',NULL);
insert into test values ('3','book',NULL);
whenever there is a book, I'm trying assign an auto increment beginning with 1 in the bookid column. For each user, the numbering begins again from 1. I know a way this can be done by creating a separate table. Is there a way I can avoid that and accomplish that using some sort of update query in this very table and update the column bookid? I am trying to get output similar to the following:
userid,item,bookid
'1','journal',NULL
'1','journal',NULL
'1','book',1
'2','book',1
'2','journal',NULL
'1','book',2
'2','journal',NULL
'3','book',1
'1','book',3
'1','journal',NULL
'3','journal',NULL
'1','journal',NULL
'2','journal',NULL
'2','book',2
'2','journal',NULL
'1','journal',NULL
'3','book',2
'3','book',3
'3','book',4
'3','book',5
I appreciate if someone could guide me on how to accomplish this?
Here's one idea...
drop table if exists test;
create table test
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,userid int not null
,item varchar(15) NOT NULL
);
insert into test (userid,item) values
(1,'journal')
,(1,'journal')
,(1,'book')
,(2,'book')
,(2,'journal')
,(1,'book')
,(2,'journal')
,(3,'book')
,(1,'book')
,(1,'journal')
,(3,'journal')
,(1,'journal')
,(2,'journal')
,(2,'book')
,(2,'journal')
,(1,'journal')
,(3,'book')
,(3,'book')
,(3,'book')
,(3,'book');
SELECT x.*
, COUNT(*) rank
FROM test x
JOIN test y
ON y.userid = x.userid
AND y.item = x.item
AND y.id <= x.id
GROUP
BY id
ORDER
BY userid
, item
, rank;
+----+--------+---------+------+
| id | userid | item | rank |
+----+--------+---------+------+
| 3 | 1 | book | 1 |
| 6 | 1 | book | 2 |
| 9 | 1 | book | 3 |
| 1 | 1 | journal | 1 |
| 2 | 1 | journal | 2 |
| 10 | 1 | journal | 3 |
| 12 | 1 | journal | 4 |
| 16 | 1 | journal | 5 |
| 4 | 2 | book | 1 |
| 14 | 2 | book | 2 |
| 5 | 2 | journal | 1 |
| 7 | 2 | journal | 2 |
| 13 | 2 | journal | 3 |
| 15 | 2 | journal | 4 |
| 8 | 3 | book | 1 |
| 17 | 3 | book | 2 |
| 18 | 3 | book | 3 |
| 19 | 3 | book | 4 |
| 20 | 3 | book | 5 |
| 11 | 3 | journal | 1 |
+----+--------+---------+------+
Note that MyISAM actually lets you use a composite PK in which part of that composite is an auto-incrementing id, but InnoDB prohinits this.
On larger datasets a query along these lines will likely be far more efficient...
SELECT id
, userid
, item
, IF(#userid=userid,IF(#item=item,#i:=#i+1,#i:=1),#i:=1) rank
, #userid := userid
, #item := item
FROM test
, (SELECT #userid = NULL,#item:='',#i:=1) vars
ORDER
BY userid,item,id;
I have a table structure like this
CREATE TABLE `test` (
`id` int(11) NOT NULL,
`userid` int(11) DEFAULT NULL,
`loan` int(11) DEFAULT NULL,
`name` varchar(90) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
+----+--------+------+--------+
| id | userid | loan | name |
+----+--------+------+--------+
| 1 | 1 | 100 | x |
| 2 | 1 | 200 | X|
| 3 | 2 | 2000 | y|
| 4 | 3 | 1000 | z|
| 5 | 1 | 500 | a|
| 6 | 2 | 700 | b|
As you can see userid is repeating.For example userid 1 is having loan 100,200 and 500
My requirement is to get userid,loan and name and if the userid is repating then loan will be the sum of the repeating userid and name should be space
for example,In the above table userid 1 is repeating so sum is (100+200+500=800)
similarly for userid 2 the sum is 2700
The output i want should be like the below
+--------+-----------+----+
| userid | SUM(loan) |name|
+--------+-----------+----+
| 1 | 800 | |
| 2 | 2700 | |
| 3 | 1000 |z |
+--------+-----------+
I can do with userid and loan but I dont know how to put a space in name field if the userid is repeating
I tried like this
SELECT userid,SUM(loan) FROM
testforsum
GROUP BY userid
and the ouput I am getting is like this
+--------+-----------+
| userid | SUM(loan) |
+--------+-----------+
| 1 | 800 |
| 2 | 2700 |
| 3 | 1000 |
+--------+-----------+
I tried to create a sqlfiddle for it but I dont know why insertion is not happening.You can see the table here
SELECT userid, IF(COUNT(*) > 1, ' ', name) AS name, SUM(loan)
FROM testforsum GROUP BY userid;
select q.userid, q.s,
case when q.n > 1 then ' ' else q.name end
from
(SELECT userid, SUM(loan) s, count(loan) n, max(name) name
FROM testforsum GROUP BY userid) q;
Some explanation:
(...) q is a subquery which calculates for each userId sum of loans (NULLS are ignored), number of loans (NULLS are ignored).
max(name) it's a "fake" aggregate function, I used it to get any name for each userId (they are all the same for each userId so I can do that) because in ANSI SQL this query is wrong:
SELECT userid, IF(COUNT(*) > 1, ' ', name) AS name, SUM(loan)
FROM testforsum GROUP BY userid;
as all the expressions in the SELECT list should be either aggregate functions (for several rows return a single value) or expressions from the GROUP BY clause. As I know MySQL lets you break these rules, but I prefer to follow ANSI whenever it's possible.
So the subquery q results in a table with unique userIds with their names + sum and number of their loans.
Finally, the parent query filters the result of the q subquery using CASE.