I have two tables, A and B, structured as follows.
CREATE TABLE TableA (
`id` INTEGER,
`code` VARCHAR(1),
`type` VARCHAR(1)
);
INSERT INTO TableA
(`id`, `code`, `type`)
VALUES
('1', 'A', 'r'),
('2', 'A', 'o'),
('3', 'A', 'u'),
('4', 'B', 'h');
CREATE TABLE TableB (
`id` INTEGER,
`sid` INTEGER,
`code` VARCHAR(1)
);
INSERT INTO TableB
(`id`, `sid`, `code`)
VALUES
('1', '1', 'A'),
('1', '1', 'A'),
('1', '1', 'A'),
('2', '1', 'B'),
('2', '1', 'B'),
('1', '2', 'A'),
('1', '2', 'A'),
('2', '2', 'A');
the output of this query should be like this
sid r o u
1 3 2 0
2 2 1 0
table A has a foreign key from table B which is the id. what I want to get is how many times each sid accessed each type of table A.
Left join Table B to Table A and aggregate:
select b.sid,
sum(a.type = 'r') r,
sum(a.type = 'o') o,
sum(a.type = 'u') u
from TableB b left join TableA a
on b.id = a.id
group by b.sid
See the demo.
Results:
| sid | r | o | u |
| --- | --- | --- | --- |
| 1 | 3 | 2 | 0 |
| 2 | 2 | 1 | 0 |
Related
Please help me.
I have 2 table Users and Diseaselogs
1 user has many diseaselogs
I want to select all users and each user must be field date in diseaselogs is max
Data example
Users
id | name | age
1 | Nam | 21
2 | Thao | 23
3 | An | 19
Diseaselogs
id | logType | userId | date
1 | positive | 1 | 2021-06-21
2 | negative | 2 | 2021-06-22
3 | pending | 1 | 2021-06-24
4 | negative | 1 | 2021-06-26
5 | negative | 2 | 2021-06-21
6 | pending | 3 | 2021-06-23
7 | negative | 1 | 2021-06-24
8 | negative | 2 | 2021-06-25
9 | pending | 3 | 2021-06-28
Expect output
id | name | logId | logType | date
1 | Nam | 4 | negative | 2021-06-26
2 | Thao | 8 | negative | 2021-06-25
3 | An | 9 | pending | 2021-06-28
As ROW_NUMBER() doesn't support lower version of mysql i.e 5.7 to downwords. So I've tried here alternatives of ROW_NUMBER().
-- MySQL (V 5.6)
SELECT u.id, u.name, tmp.logId, tmp.logType, tmp.date
FROM users u
INNER JOIN (SELECT #row_no := IF(#prev_val = t.userId, #row_no + 1, 1) AS row_number
, #prev_val := t.userId AS userId
, t.date
, t.logType
, t.id AS logId
FROM Diseaselogs t,
(SELECT #row_no := 0) x,
(SELECT #prev_val := '') y
ORDER BY t.userId, t.date DESC ) tmp
ON u.id = tmp.userId
AND tmp.row_number = 1;
Please check url http://sqlfiddle.com/#!9/741b96/16
Also if your MySQL version is 5.8 then you can apply the below query where row_number() is used.
SELECT u.id, u.name, tmp.logId, tmp.logType, tmp.date
FROM users u
INNER JOIN (SELECT ROW_NUMBER() OVER (PARTITION BY userId ORDER BY date DESC) row_number
, userId
, date
, logType
, id AS logId
FROM Diseaselogs ) tmp
ON u.id = tmp.userId
AND tmp.row_number = 1;
in mysql 8 you can use ROW_NUMBER and an INNER JOIN
in prior versions you need to use another method for rownumber
Schema (MySQL v8.0)
CREATE TABLE Diseaselogs (
`id` INTEGER,
`logType` VARCHAR(8),
`userId` INTEGER,
`date` VARCHAR(10)
);
INSERT INTO Diseaselogs
(`id`, `logType`, `userId`, `date`)
VALUES
('1', 'positive', '1', '2021-06-21'),
('2', 'negative', '2', '2021-06-22'),
('3', 'pending', '1', '2021-06-24'),
('4', 'negative', '1', '2021-06-26'),
('5', 'negative', '2', '2021-06-21'),
('6', 'pending', '3', '2021-06-23'),
('7', 'negative', '1', '2021-06-24'),
('8', 'negative', '2', '2021-06-25'),
('9', 'pending', '3', '2021-06-28');
CREATE TABLE users (
`id` INTEGER,
`name` VARCHAR(4),
`age` INTEGER
);
INSERT INTO users
(`id`, `name`, `age`)
VALUES
('1', 'Nam', '21'),
('2', 'Thao', '23'),
('3', 'An', '19');
Query #1
SELECT
u.id, `name`, t1.id,`logType`, `date`
FROM
(SELECT
id,`logType`, `userId`, `date`
, ROW_NUMBER() OVER (PARTITION BY `userId` ORDER BY `date` DESC) rn
FROM
Diseaselogs) t1
INNER JOIN users u ON t1.userId = u.id
WHERE rn = 1;
id
name
id
logType
date
1
Nam
4
negative
2021-06-26
2
Thao
8
negative
2021-06-25
3
An
9
pending
2021-06-28
View on DB Fiddle
Schema (MySQL v8.0)
CREATE TABLE Diseaselogs (
`id` INTEGER,
`logType` VARCHAR(8),
`userId` INTEGER,
`date` VARCHAR(10)
);
INSERT INTO Diseaselogs
(`id`, `logType`, `userId`, `date`)
VALUES
('1', 'positive', '1', '2021-06-21'),
('2', 'negative', '2', '2021-06-22'),
('3', 'pending', '1', '2021-06-24'),
('4', 'negative', '1', '2021-06-26'),
('5', 'negative', '2', '2021-06-21'),
('6', 'pending', '3', '2021-06-23'),
('7', 'negative', '1', '2021-06-24'),
('8', 'negative', '2', '2021-06-25'),
('9', 'pending', '3', '2021-06-28');
CREATE TABLE users (
`id` INTEGER,
`name` VARCHAR(4),
`age` INTEGER
);
INSERT INTO users
(`id`, `name`, `age`)
VALUES
('1', 'Nam', '21'),
('2', 'Thao', '23'),
('3', 'An', '19');
Query #1
SELECT
u.id, `name`, `age`,`logType`, `date`
FROM
(SELECT
`logType`, `userId`, `date`
, ROW_NUMBER() OVER (PARTITION BY `userId` ORDER BY `date` DESC) rn
FROM
Diseaselogs) t1
INNER JOIN users u ON t1.userId = u.id
WHERE rn = 1;
id
name
age
logType
date
1
Nam
21
negative
2021-06-26
2
Thao
23
negative
2021-06-25
3
An
19
pending
2021-06-28
View on DB Fiddle
table proyectos has two related columns, id_pro and id_padre
CREATE TABLE IF NOT EXISTS `proyectos` (
`id_pro` int(8) unsigned NOT NULL,
`ot_padre` int(8) unsigned NOT NULL,
`nom_pro` varchar(255) NOT NULL,
PRIMARY KEY (`id_pro`)
) DEFAULT CHARSET=utf8;
INSERT INTO `proyectos` (`id_pro`, `ot_padre`, `nom_pro`) VALUES
('1', '0', 'Proyecto 1'),
('2', '0', 'Proyecto 2'),
('3', '0', 'Proyecto 3'),
('4', '3', 'Proyecto hijo 1'),
('5', '3', 'Proyecto hijo 2');
CREATE TABLE IF NOT EXISTS `servicios` (
`id_ser` int(8) unsigned NOT NULL,
`id_pro` int(8) unsigned NOT NULL,
`nom_ser` varchar(255) NOT NULL,
PRIMARY KEY (`id_ser`)
) DEFAULT CHARSET=utf8;
INSERT INTO `servicios` (`id_ser`, `id_pro`, `nom_ser`) VALUES
('1', '1', 'Servicio a'),
('2', '1', 'Servicio b'),
('3', '3', 'Servicio c'),
('4', '3', 'Servicio d'),
('5', '4', 'Servicio e'),
('6', '5', 'Servicio e');
How can obtain all data from servicios with id_pro 3, and also all service with id_padre 3?
EDIT**
expected output
id_ser | id_pro | nom_ser |
3 | 3 | Servicio c |
4 | 3 | Servicio d |
5 | 4 | Servicio e |
6 | 5 | Servicio f |
schema and query
http://sqlfiddle.com/#!9/a1e0dc/1/0
You need an INNER join of the tables:
SELECT s.*
FROM servicios s INNER JOIN proyectos p
ON p.id_pro = s.id_pro
WHERE 3 IN (p.id_pro, p.ot_padre)
See the demo.
Results:
id_ser
id_pro
nom_ser
3
3
Servicio c
4
3
Servicio d
5
4
Servicio e
6
5
Servicio f
SELECT DISTINCT * FROM Servicios
WHERE id_pro = 3
OR id_pro IN
(SELECT id_pro
FROM Proyectos
WHERE id_padre = 3)
Outputs:
id_ser id_pro nom_ser
3 3 Servicio c
4 3 Servicio d
5 4 Servicio e
It seems to me that you're actually after this:
SELECT *
FROM servicios
WHERE id_pro = 3
UNION
SELECT s.*
FROM servicios s
JOIN proyectos p
ON p.id_pro = s.id_pro
WHERE p.ot_padre = 3;
(There are various ways of writing this, including a LEFT JOIN with COALESCE)
i have 2 tables (tbl_news and tbl_news_category)
tbl_news_category table is
cat_id cat_name general
1 name1 1
2 name2 1
3 name3 0
4 name4 1
5 name5 0
tbl_news table is
id cat_id title news_isimage
1 2 title1 1
2 5 title2 0
3 1 title2 1
4 3 title2 1
5 2 title2 1
6 5 title2 1
7 4 title2 1
8 5 title2 1
i want to get 5 random items from tbl_news which general value should be 1
i tried below code and its not working
SELECT
d.*
FROM
tbl_news d,
tbl_news_category p
WHERE
p.general = 1 AND d.news_isimage = 0
AND d.cat_id > 3
ORDER BY RAND()
LIMIT 5
but its giving news item with cat_id with general = 0
Do a proper join of the tables:
select n.*
from tbl_news n inner join tbl_news_category c
on c.cat_id = n.cat_id
where c.general = 1
order by rand() limit 5
You have also in your code the conditions:
news_isimage = 0 AND cat_id > 3
If you need them you can add them to the WHERE clause:
WHERE c.general = 1 AND n.news_isimage = 0 AND c.cat_id > 3
There is no data for your query available.
But do an INNER JOIN
CREATE TABLE tbl_news (
`id` INTEGER,
`cat_id` INTEGER,
`title` VARCHAR(6),
`news_isimage` INTEGER
);
INSERT INTO tbl_news
(`id`, `cat_id`, `title`, `news_isimage`)
VALUES
('1', '2', 'title1', '1'),
('2', '5', 'title2', '0'),
('3', '1', 'title2', '1'),
('4', '3', 'title2', '1'),
('5', '2', 'title2', '1'),
('6', '5', 'title2', '1'),
('7', '4', 'title2', '1'),
('8', '5', 'title2', '1');
CREATE TABLE tbl_news_category (
`cat_id` INTEGER,
`cat_name` VARCHAR(5),
`general` INTEGER
);
INSERT INTO tbl_news_category
(`cat_id`, `cat_name`, `general`)
VALUES
('1', 'name1', '1'),
('2', 'name2', '1'),
('3', 'name3', '0'),
('4', 'name4', '1'),
('5', 'name5', '0');
✓
✓
✓
✓
SELECT
tn.title
, tn.news_isimage
,tnc.cat_name
FROM tbl_news tn INNER JOIN tbl_news_category tnc ON tn.cat_id = tnc.cat_id
WHERE
tnc.general = 1
AND tn.news_isimage = 0
AND tnc.cat_id > 3
ORDER BY RAND()
LIMIT 5
title | news_isimage | cat_name
:---- | -----------: | :-------
db<>fiddle here
I have three tables to keep track of emails and their assigned categories: Email keeps the mail's content, Category lists the categories and Classification links an Email entry ID with a Category entry ID. Schema with sample data and query is available on SQLFiddle: http://sqlfiddle.com/#!9/a410a6/26/0
CREATE TABLE `Category` (
`id` int(6) unsigned NOT NULL,
`name` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
CREATE TABLE `Mail` (
`id` int(6) unsigned NOT NULL,
`content` varchar(500) NOT NULL,
`date` datetime NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
CREATE TABLE `Classification` (
`id` int(6) unsigned NOT NULL,
`mail_id` int(6) unsigned NOT NULL,
`category_id` int(6) unsigned NOT NULL,
FOREIGN KEY (mail_id) REFERENCES Mail(id),
FOREIGN KEY (category_id) REFERENCES Category(id),
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `Category` (`id`, `name`) VALUES
('1', 'Important'),
('2', 'Urgent'),
('3', 'Normal');
INSERT INTO `Mail` (`id`, `content`, `date`) VALUES
('1', 'Important Email', '2019-01-04T13:53:52'),
('2', 'Urgent Email', '2019-01-19T13:53:52'),
('3', 'Very Urgent Email', '2019-01-24T13:53:52'),
('4', 'Quite Urgent Email', '2019-01-24T13:53:52'),
('5', 'Normal Email', '2019-01-21T13:53:52'),
('6', 'Regular Email', '2019-01-14T13:53:52'),
('7', 'Regular Email', '2019-01-23T13:53:52'),
('8', 'Regular Email', '2019-01-23T13:53:52'),
('9', 'Regular Email', '2019-01-20T13:53:52'),
('10', 'Very Urgent Email', '2019-01-25T13:53:52'),
('11', 'Very Urgent Email', '2019-01-25T13:53:52');
INSERT INTO `Classification` (`id`, `mail_id`, `category_id`) VALUES
('1', '1', '1'),
('2', '2', '2'),
('3', '3', '2'),
('4', '4', '2'),
('5', '5', '3'),
('6', '6', '3'),
('7', '6', '3'),
('8', '6', '3'),
('9', '6', '3'),
('10', '6', '2'),
('11', '6', '2');
I want to return the number of mails received for each category for each date recorded, i.e. my expected results would be
+----------------------+-----------+----------+
| date | name | count(*) |
+----------------------+-----------+----------+
| 2019-01-04T13:53:52Z | Important | 1 |
| 2019-01-14T13:53:52Z | Normal | 1 |
| 2019-01-19T13:53:52Z | Urgent | 1 |
| 2019-01-20T13:53:52Z | Normal | 1 |
| 2019-01-21T13:53:52Z | Normal | 1 |
| 2019-01-23T13:53:52Z | Normal | 2 |
| 2019-01-24T13:53:52Z | Urgent | 1 |
| 2019-01-25T13:53:52Z | Urgent | 2 |
+----------------------+-----------+----------+
To do so I run the following query with a double groupby, filtering on the Classification table:
SELECT Mail.date, Category.name, count(*) FROM Mail, Classification, Category WHERE Category.id = Classification.category_id AND Classification.mail_id = Mail.id GROUP BY Mail.date, Category.name
Which gives me the following results:
+----------------------+-----------+----------+
| date | name | count(*) |
+----------------------+-----------+----------+
| 2019-01-04T13:53:52Z | Important | 1 |
| 2019-01-14T13:53:52Z | Normal | 4 |
| 2019-01-14T13:53:52Z | Urgent | 2 |
| 2019-01-19T13:53:52Z | Urgent | 1 |
| 2019-01-21T13:53:52Z | Normal | 1 |
| 2019-01-24T13:53:52Z | Urgent | 2 |
+----------------------+-----------+----------+
Which is entirely wrong.
I've tried substituting the WHERE statement for a JOIN:
SELECT Mail.date, Category.name, count(*) FROM (Mail, Category) RIGHT JOIN Classification ON Category.id = Classification.category_id AND Classification.mail_id = Mail.id GROUP BY Mail.date, Category.name `
But I get the exact same results as above.
Why are those queries returning these erroneous results and what should I do to fix them ?
First, your query should look like this:
SELECT m.date, c.name, count(*)
FROM Mail m JOIN
Classification cl
ON cl.mail_id = m.id JOIN
Category c
ON c.id = cl.category_id
GROUP BY m.date, c.name ;
Now that we have gotten that out of the way, your problem is that emails have multiple categories. So, they are multiply counted. Hence, the results you are getting are correct.
You have exact duplicates in the classification table, so a simple solution is:
SELECT m.date, c.name, count(distinct m.id)
FROM Mail m JOIN
Classification cl
ON cl.mail_id = m.id JOIN
Category c
ON c.id = cl.category_id
GROUP BY m.date, c.name ;
That said, the real solution is to fix your data, so it doesn't have duplicates.
Here is the SQL Fiddle using your data. You have a "2" for emails on 2019-01-23. However, there are no classified emails on that date, so they are not in the results.
I have a table which looks like this,
+-----+--------+-------+
| num | amount | value |
+-----+--------+-------+
| 1 | 12 | 1 |
| 1 | 12 | 1 |
| 2 | 13 | 1 |
| 4 | 15 | 0 |
| 2 | 13 | 1 |
| 3 | 14 | 1 |
| 3 | 14 | 1 |
| 1 | 12 | 1 |
+-----+--------+-------+
I want to sum the 'amount' column based on distinct 'num' column where 'value' is equal to 1,
for example, after running following query,
select DISTINCT num, amount, value from test where value =1 ;
distinct 'num' based table is
+-----+--------+-------+
| num | amount | value |
+-----+--------+-------+
| 1 | 12 | 1 |
| 2 | 13 | 1 |
| 3 | 14 | 1 |
+-----+--------+-------+
so i want the final result to be
12+13+14 = 39.
one more thing is that
i cant use subquery.
because it is already part of another query as well.
here is the script of my table is
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `test`
-- ----------------------------
DROP TABLE IF EXISTS `test`;
CREATE TABLE `test` (
`num` int(11) DEFAULT NULL,
`amount` int(11) DEFAULT NULL,
`value` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- ----------------------------
-- Records of test
-- ----------------------------
INSERT INTO `test` VALUES ('1', '12', '1');
INSERT INTO `test` VALUES ('1', '12', '1');
INSERT INTO `test` VALUES ('2', '13', '1');
INSERT INTO `test` VALUES ('4', '15', '0');
INSERT INTO `test` VALUES ('2', '13', '1');
INSERT INTO `test` VALUES ('3', '14', '1');
INSERT INTO `test` VALUES ('3', '14', '1');
INSERT INTO `test` VALUES ('1', '12', '1');
You can create a temp table containing distinct values and using SUM() to calculate amount. Your query will go like:
SELECT SUM(amount)
FROM (
SELECT DISTINCT t.`num`, t.`amount`
FROM test t
WHERE t.`value`=1
) temp
select num,sum(amount)/Count(*)
from test
where value = 1
group by num
order by num
I assume that every num has the same value (if not, how should you pick one?)
so, this will work:
SELECT SUM(DISTINCT amount)
FROM test
WHERE value = 1
SELECT SUM(amount) FROM (SELECT DISTINCT num, amount, VALUE FROM test WHERE VALUE =1) AS temp ;
Please check the solution
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `test`
-- ----------------------------
DROP TABLE IF EXISTS `test`;
CREATE TABLE `test` (
`num` int(11) DEFAULT NULL,
`amount` int(11) DEFAULT NULL,
`value` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
-- ----------------------------
-- Records of test
-- ----------------------------
INSERT INTO `test` VALUES ('1', '12', '1');
INSERT INTO `test` VALUES ('1', '12', '1');
INSERT INTO `test` VALUES ('2', '13', '1');
INSERT INTO `test` VALUES ('4', '15', '0');
INSERT INTO `test` VALUES ('2', '13', '1');
INSERT INTO `test` VALUES ('3', '14', '1');
INSERT INTO `test` VALUES ('3', '14', '1');
INSERT INTO `test` VALUES ('1', '12', '1');
-- ----------------------------
-- Insert the result set into temp table and then use sum().
-- ----------------------------
SELECT DISTINCT `num` , `amount`
INTO #tmp
FROM test
WHERE `value` =1
SELECT * FROM #tmp
SELECT sum(`amount`) as amount FROM #tmp