mysql> create table a (
-> id varchar(10),
-> val int
-> );
Query OK, 0 rows affected (0.02 sec)
mysql> create table b (
-> id varchar(10),
-> val int
-> );
Query OK, 0 rows affected (0.02 sec)
mysql> insert into a values ('a', 1), ('b', 2), ('c', 3);
mysql> insert into b values ('a', 4), ('a', 5), ('b', 6), ('b', 7), ('c', 8), ('c', 9);
mysql> select a.id, sum(a.val), sum(b.val) from a inner join b on a.id = b.id group by a.id;
+------+------------+------------+
| id | sum(a.val) | sum(b.val) |
+------+------------+------------+
| a | 2 | 9 |
| b | 4 | 13 |
| c | 6 | 17 |
+------+------------+------------+
3 rows in set (0.00 sec)
My expected result was for sum(a.val) to present 1, 2 and 3, sum(b.val) to present 9, 13 and 17.
How should I rewrite the query to get the expected result?
It is due to the JOIN with the other table.
If you just run the simple query with the JOINS but without the SUM, you will see that the records in Table A are being doubled, because you INNER JOIN with TABLE B.
A.ID A.val B.val
a 1 4
a 1 5
b 2 6
b 2 7
c 3 8
c 3 9
In order to get the expected result, you will have to query like this:
SELECT
A.ID,
A.VAL 'A Sum',
B.VAL 'B Sum'
FROM
(SELECT ID, SUM(VAL) AS 'VAL' FROM A GROUP BY ID) A INNER JOIN
(SELECT ID, SUM(VAL) AS 'VAL' FROM B GROUP BY ID) B ON A.ID = B.ID
Here is a SQLFiddle with how this query works.
Here is the result of the join query before doing the group by and sum:
a.id a.val b.val
---- ----- -----
a 1 4
a 1 5
b 2 6
b 2 7
c 3 8
c 3 9
As you see here, the sum(a.val) for all rows with a.id='a' will give you 2 since there are 2 rows. So the results, are [2, 4, 6], not [1, 2, 3].
Related
I have a table named question_dispositions composed like this
Type | chapter | number
-------------------------
A | 1 | 3
B | 1 | 2
B | 4 | 1
The second table named question has columns type, text and chapter.
Type | chapter | text
-----------------------
A | 1 | T1
A | 1 | T2
B | 1 | T3
B | 1 | T4
B | 1 | T5
B | 2 | T6
B | 2 | T7
B | 3 | T8
B | 4 | T9
What I need is to fetch X random questions from table Question where the X is the column number from question_dispositions
So if I need questions of type B the result that I expect is this:
chapter | text
--------------
1 | T3
1 | T4
4 | T9
How can I Select random question limiting by the column number in another table? I tried with this query but returns every row in the table Question as expected because I can not limit with qd.count
SELECT * FROM
question_dispositions AS qd, question AS q
WHERE qd.chapter = q.chapter AND qd.type = q.type AND qd.type = 'B'
ORDER BY RAND() is a typical approach to randomness in MySQL but it is usually criticized because it does not scale well. Here however you appear to be selecting a quite small number of rows as questions but to achieve some randomness of those. So, here is an approach using ORDER BY RAND() which should be acceptable in performance for small result sets.
There is an added requirement to LIMIT by a stored Number, however in the small example supplied that number isn't consistent for type B so I chose to limit by the maximum of that number for the wanted type B. MySQL doesn't allow use of variables or subqueries with the LIMIT clause so instead a generated row number is used in a where clause to achieve that outcome.
Refer to this SQL Fiddle
Data:
CREATE TABLE question_dispositions
(`Type` varchar(1), `chapter` int, `number` int)
;
INSERT INTO question_dispositions
(`Type`, `chapter`, `number`)
VALUES
('A', 1, 3),
('B', 1, 2),
('B', 4, 1)
;
CREATE TABLE question
(`Type` varchar(1), `chapter` int, `text` varchar(2))
;
INSERT INTO question
(`Type`, `chapter`, `text`)
VALUES
('A', 1, 'T1'),
('A', 1, 'T2'),
('B', 1, 'T3'),
('B', 1, 'T4'),
('B', 1, 'T5'),
('B', 2, 'T6'),
('B', 2, 'T7'),
('B', 3, 'T8'),
('B', 4, 'T9')
;
Query:
SELECT *
FROM (
SELECT #rownum := #rownum + 1 AS rn , q.*
FROM question_dispositions AS qd
INNER JOIN question AS q ON qd.chapter = q.chapter AND qd.type = q.type
CROSS JOIN (SELECT #rownum := 0) r
WHERE qd.type = 'B'
ORDER BY RAND()
) d
WHERE rn <= (SELECT MAX(NUMBER) FROM question_dispositions WHERE type = 'B')
Example Results:
| rn | Type | chapter | text |
|----|------|---------|------|
| 1 | B | 1 | T3 |
| 2 | B | 1 | T4 |
There are 5 columns X and A, B, C, D in table t. Columns A, B, C, D are varchar. and column X has to show us how many of the next row repeating characters. I need help to update column X.
Example :
|ID | X | A | B | C | D |
=========================
| 4 | 1 | 7 | J | 7 | Q |
| 3 | 2 | K | Q | 8 | 8 |
| 2 | 3 | 7 | 8 | 9 | J | next row X=3
| 1 | 0 | 7 | J | 8 | K | 0 default
ID-1 is the first and X is zero by default and from there begin calculations ID-2 and X=3 because we have ID-1 "7" "J" and "8" and the next row ID-2 have the combination "7 8 9 J" in ID-1 there "7 J 8 -" and X should be 3. Values of X can be between 0 and 4. ID-3, X=2 "- - 8 8" because in ID-2 have the combination "7 8 9 J" and i have 8 - twice in "K Q 8 8".
Added : 08/06
http://sqlfiddle.com/#!2/4586e/1
CREATE TABLE tmp
(
id int,
alnum CHAR(1),
cnt int,
PRIMARY KEY (id, alnum)
);
INSERT INTO tmp (id, alnum, cnt) SELECT id, A, 1 FROM tab ON DUPLICATE KEY UPDATE cnt = cnt + 1;
INSERT INTO tmp (id, alnum, cnt) SELECT id, B, 1 FROM tab ON DUPLICATE KEY UPDATE cnt = cnt + 1;
INSERT INTO tmp (id, alnum, cnt) SELECT id, C, 1 FROM tab ON DUPLICATE KEY UPDATE cnt = cnt + 1;
INSERT INTO tmp (id, alnum, cnt) SELECT id, D, 1 FROM tab ON DUPLICATE KEY UPDATE cnt = cnt + 1;
UPDATE tab INNER JOIN (
SELECT t1.id AS id, SUM(t2.cnt) AS sum
FROM tmp t1 INNER JOIN tmp t2 ON t1.id + 1 = t2.id
AND t1.alnum = t2.alnum
GROUP BY t1.id
) tmp3 ON tab.id = tmp3.id + 1
SET tab.X = tmp3.sum;
SELECT * FROM tab WHERE X > 4;
Original answer : 08/05
How about using tmp table which stores for each A, B, C, D columns into single rows. This makes us easy to calculate X value.
Below code assumes id is sequencial value. If not, please let me know there is another query for it.
CREATE TABLE tab
(
id INT,
X INT,
A CHAR(1),
B CHAR(1),
C CHAR(1),
D CHAR(1)
);
INSERT INTO tab VALUES (1, 0, '7', 'J', '8', 'K');
INSERT INTO tab VALUES (2, 0, '7', '8', '9', 'J');
INSERT INTO tab VALUES (3, 0, 'K', 'Q', '8', '8');
INSERT INTO tab VALUES (4, 0, '7', 'J', '7', 'Q');
CREATE TABLE tmp
(
id INT,
alnum CHAR(1)
);
INSERT INTO tmp SELECT id, A FROM tab;
INSERT INTO tmp SELECT id, B FROM tab;
INSERT INTO tmp SELECT id, C FROM tab;
INSERT INTO tmp SELECT id, D FROM tab;
UPDATE tab INNER JOIN (
SELECT t1.id AS id, COUNT(*) AS cnt
FROM tmp t1 INNER JOIN tmp t2 ON t1.id + 1 = t2.id
AND t1.alnum = t2.alnum
GROUP BY t1.id
) tmp3 ON tab.id = tmp3.id + 1
SET tab.X = tmp3.cnt;
mysql> SELECT * FROM tab ORDER BY id DESC;
+------+------+------+------+------+------+
| id | X | A | B | C | D |
+------+------+------+------+------+------+
| 4 | 1 | 7 | J | 7 | Q |
| 3 | 2 | K | Q | 8 | 8 |
| 2 | 3 | 7 | 8 | 9 | J |
| 1 | 0 | 7 | J | 8 | K |
+------+------+------+------+------+------+
4 rows in set (0.00 sec)
The X values in your example are from the "next" row, not the previous. Assuming you have an auto-incrementing id, you can calculate this information by looking at the next row. You can generate a query to do the calculation:
select ((case when t1.A in (tnext.A, tnext.B, tnext.C, tnext.D) then 1 else 0 end) +
(case when t1.B in (tnext.A, tnext.B, tnext.C, tnext.D) then 1 else 0 end) +
(case when t1.C in (tnext.A, tnext.B, tnext.C, tnext.D) then 1 else 0 end) +
(case when t1.D in (tnext.A, tnext.B, tnext.C, tnext.D) then 1 else 0 end)
) as X, t1.*
from (select t.*, (select max(id) from table t2 where t2.id > t.id) as nextid
from table t
) t1 left outer join
t tnext
on tnext.id = t1.nextid;
Depending on the database you are using, this code can be simplified and expressed differently. Also, the specific update syntax might depend on the database.
When I left join the following tables, I get the results for all the id's. I need to exclude the results where there is no single id present in sms table.
So the expected output is as follows:
+-----------+-----------+
| messageid | mobilenos |
+-----------+-----------+
| a | 12 |
| c | 31 |
+-----------+-----------+
2 rows in set (0.00 sec)
The messageid "d" should not be displayed in the output because there is not a single entry for "d" in the sms table.
I will like to know if the following query is correct or if there is a better way:
select a.* from splitvalues as a
left join sms as b on a.messageid = b.batchid and a.mobilenos = b.destination
left join (select a.messageid from splitvalues as a left join sms as b on a.messageid = b.batchid where b.batchid is null) as dt on dt.messageid = a.messageid where dt.messageid is null and b.destination is null;
Following are the table details:
splitvalues
messageid mobilenos
a 10
a 11
a 12
b 20
b 21
b 22
b 23
b 24
c 30
c 31
d 40
d 41
d 42
d 43
sms
batchid destination
a 10
a 11
b 20
b 21
b 22
b 23
b 24
c 30
drop table if exists splitvalues;
drop table if exists sms;
create table if not exists splitvalues (messageid varchar(255), mobilenos int);
create table if not exists sms (batchid varchar(255), destination int);
insert into splitvalues values ('a', 10), ('a', 11), ('a', 12), ('b', 20), ('b', 21), ('b', 22), ('b', 23), ('b', 24), ('c', 30), ('c', 31), ('d', 40), ('d', 41), ('d', 42), ('d', 43);
insert into sms values ('a', 10), ('a', 11), ('b', 20), ('b', 21), ('b', 22), ('b', 23), ('b', 24), ('c', 30);
mysql> select a.* from splitvalues as a left join sms as b on a.messageid = b.batchid and a.mobilenos = b.destination where b.destination is null;
+-----------+-----------+
| messageid | mobilenos |
+-----------+-----------+
| a | 12 |
| c | 31 |
| d | 40 |
| d | 41 |
| d | 42 |
| d | 43 |
+-----------+-----------+
6 rows in set (0.00 sec)
Try This...
select a.* from [dbo].[splitvalues] a join [dbo].[sms] b on a.messageid=b.batchid
Or
select a.* from [dbo].[splitvalues] a ,[dbo].[sms] b where a.messageid=b.batchid
Try inner join it will produce rows which exists in both table,
select * from splitvalues as a
inner join sms as b on a.messageid = b.batchid and a.mobilenos = b.destination
select * from
splitvalues
where mobilenos not in(select destination from sms) limit 2;
To assist in understanding I have a table like this:
itemcode itemname icode serialnum
1 A 10 0
2 B 10 0
3 C 10 0
4 D 11 0
5 E 13 0
6 F 20 0
7 G 20 0
I want the result to look like the table below using a single update query with the help of looping cursors:
itemcode itemname icode serialnum
1 A 10 1
2 B 10 2
3 C 10 3
4 D 11 1
5 E 13 1
6 F 20 1
7 G 20 2
Item code is the primary key in this table. The logic behind generating the serial number is whenever icode changes the serial number gets reset to 1. I need a single update query to update the table with the help of cursors if someone can assist with a solution?
So you want a sequential serial number for every icode-group. Then you can use ROW_NUMBER with PARTITION BY icode:
WITH CTE AS
(
SELECT itemcode, itemname, icode, serialnum,
, ROW_NUMBER() OVER (PARTITION BY icode ORDER BY itemcode) AS RN
FROM dbo.TableName
)
UPDATE CTE SET serialnum = RN;
If you want it to be reculculated on every update you could use a trigger:
CREATE TRIGGER dbo.TableName_Updated
ON dbo.TableName
FOR UPDATE /* Fire this trigger when one or multiple rows are UPDATEd */
AS BEGIN
WITH CTE AS
(
SELECT itemcode, itemname, icode, serialnum,
, ROW_NUMBER() OVER (PARTITION BY icode ORDER BY itemcode) AS RN
FROM dbo.TableName t INNER JOIN INSERTED i
ON t.itemcode = i.itemcode
)
UPDATE CTE SET serialnum = RN
END
SQL Fiddle
MS SQL Server 2008 Schema Setup:
create table Item
(
itemcode int,
itemname char(1),
icode int,
serialnum int
)
insert into Item values
(1, 'A', 10, 0),
(2, 'B', 20, 0),
(3, 'C', 11, 0),
(4, 'D', 10, 0),
(5, 'E', 20, 0),
(6, 'F', 10, 0),
(7, 'G', 13, 0)
Query 1:
update I
set serialnum = rn
from
(
select serialnum,
row_number() over(partition by icode order by itemcode) as rn
from Item
) I
select *
from Item
Results:
| ITEMCODE | ITEMNAME | ICODE | SERIALNUM |
-------------------------------------------
| 1 | A | 10 | 1 |
| 2 | B | 20 | 1 |
| 3 | C | 11 | 1 |
| 4 | D | 10 | 2 |
| 5 | E | 20 | 2 |
| 6 | F | 10 | 3 |
| 7 | G | 13 | 1 |
Update
A version that uses a cursor instead of not using a cursor.
SQL Fiddle
declare #itemcode int
declare #rn int
declare ItemCursor cursor local static forward_only read_only for
select itemcode,
row_number() over(partition by icode order by itemcode) as rn
from Item
open ItemCursor
fetch next from ItemCursor
into #itemcode, #rn
while ##fetch_status = 0
begin
update Item
set serialnum = #rn
where itemcode = #itemcode
fetch next from ItemCursor
into #itemcode, #rn
end
close ItemCursor
deallocate ItemCursor
I am having a table as below:
id studentName Marks
1 X 60
2 Y 25
3 Z 50
Here the pass Marks is 50 and above
My output should be
id studentName Marks status totalCount
1 X 60 PASS 2
2 Y 25 FAIL 1
3 Z 50 PASS 2
Here the total count of pass is 2 as well as total number of fail is 1.
How can this be done using MYSQL QUERY.
thanks in advance....
SELECT A.id, A.studentName, A.Marks, B.status, B.totalStatus
FROM students AS A,
(SELECT COUNT(1) AS totalStatus,
IF(Marks>=50, 'PASS', 'FAIL') AS status
FROM students
GROUP BY IF(Marks>=50, 'PASS', 'FAIL')
) AS B
WHERE B.status = IF(A.Marks>=50, 'PASS', 'FAIL');
-- lwdba#localhost (DB test2) ::
CREATE TABLE students
(
id INT NOT NULL,
studentName VARCHAR(10),
Marks INT NOT NULL,
PRIMARY KEY (id)
);
-- Query OK, 0 rows affected (0.17 sec)
-- lwdba#localhost (DB test2) ::
INSERT INTO students VALUES
(1, 'X', 60),
(2, 'Y', 25),
(3, 'X', 50);
-- Query OK, 3 rows affected (0.06 sec)
-- Records: 3 Duplicates: 0 Warnings: 0
-- lwdba#localhost (DB test2) ::
SELECT A.id, A.studentName, A.Marks, B.status, B.totalStatus
FROM students AS A,
(SELECT COUNT(1) AS totalStatus,
IF(Marks>=50, 'PASS', 'FAIL') AS status
FROM students
GROUP BY IF(Marks>=50, 'PASS', 'FAIL')) AS B
WHERE B.status = IF(A.Marks>=50, 'PASS', 'FAIL');
+----+-------------+-------+--------+-------------+
| id | studentName | Marks | status | totalStatus |
+----+-------------+-------+--------+-------------+
| 1 | X | 60 | PASS | 2 |
| 2 | Y | 25 | FAIL | 1 |
| 3 | X | 50 | PASS | 2 |
+----+-------------+-------+--------+-------------+
3 rows in set (0.00 sec)
select s.id, s.studentName, s.Marks,
case when s.Marks >= 50 then 'Pass' else 'Fail' end as Status,
case when s.Marks >= 50 then t.TotalPass else t.TotalFail end as TotalCount
from #Students s,
(select SUM(case when Marks >= 50 then 1 else 0 end) as TotalPass,
SUM(case when Marks < 50 then 1 else 0 end) as TotalFail
from #Students) t
Use a "case" statement to translate #'s into "pass", "fail. Combine that with group by and a count(*). I'm not clear on what "marks" and "total status" is. which one is the grade? which one is the # of grades?
http://dev.mysql.com/doc/refman/5.0/en/case-statement.html