My schema looks like this:
SET GLOBAL sql_mode=(SELECT REPLACE(##sql_mode,'ONLY_FULL_GROUP_BY',''));
create table ads(
ad_id int,
ad_name varchar(10)
);
create table ads_insight(
id int,
ad_id int,
date date,
ad_clicks int
);
create table products(
id int,
name varchar(10)
);
create table products_insight(
id int,
product_id int,
sale int,
date date
);
create table ads_products(
ad_id int,
product_id int
);
insert into ads(ad_id, ad_name) values
(1,'ad1'),
(2,'ad2'),
(3,'ad3');
insert into ads_insight(id, ad_id, date, ad_clicks) values
(1, 1, '2021-04-25', 1),
(3, 1, '2021-04-23', 2),
(4, 1, '2021-04-22', 8),
(5, 2, '2021-04-25', 6),
(6, 2, '2021-03-03', 7);
insert into products(id, name) values
(1,'prod1'),
(2,'prod2'),
(3,'prod3'),
(4,'prod4'),
(5,'prod5');
insert into products_insight(id, product_id, sale, date) values
(1, 1, 10, '2021-04-25'),
(2, 1, 13, '2021-04-24'),
(3, 1, 15, '2021-04-23'),
(4, 1, 14, '2021-04-22'),
(5, 1, 17, '2021-04-21'),
(6, 1, 15, '2021-04-20'),
(7, 1, 13, '2021-04-19'),
(8, 2, 15, '2021-04-25');
insert into ads_products (ad_id, product_id) values
(1, 1),
(1, 2),
(2, 3),
(2, 4),
(2, 2),
(3, 1);
Here you have fiddle
A quick explanation of schema:
I have ads:
each ad has insights, which tell us when a certain ad was active(=> ad_clicks has to be > 0).
each ad has products(many2many - ads_products table). Each product has products_insight which tells us how many sales that product generated on a certain day.
Now I want to get all ads from the time range 2021-04-20 - 2021-04-25 which had ad_clicks > 0 (which I have done) AND count how many sales each ad has generated when it was active. So count sale only if the ad has ad_insight and ad_clicks > 0.
My query looks like this:
SET #from_date = '2021-04-20';
SET #to_date = '2021-04-25';
SELECT
ads.ad_name,
IFNULL(ad_clicks, 0) AS clicks,
IFNULL(product_sale, 0) AS product_sale,
IFNULL(products, '') AS products
FROM ads
LEFT JOIN (
SELECT ad_id, SUM(ad_clicks) AS ad_clicks
FROM ads_insight
WHERE date BETWEEN #from_date AND #to_date
GROUP BY ad_id
) AS ai ON ai.ad_id = ads.ad_id
LEFT JOIN (
SELECT ad_id, SUM(sale) AS product_sale
FROM ads_products AS ap
LEFT JOIN products_insight AS pi ON pi.product_id = ap.product_id
WHERE date BETWEEN #from_date AND #to_date
GROUP BY ad_id
) AS pi ON pi.ad_id = ads.ad_id
LEFT JOIN (
SELECT ap.ad_id, GROUP_CONCAT(DISTINCT p.name) AS products
FROM ads_products AS ap
JOIN products AS p ON ap.product_id = p.id
GROUP BY ap.ad_id
) AS p ON ads.ad_id = p.ad_id
WHERE ad_clicks>0;
And it generates the following result:
| ad_name | clicks | product_sale | products |
| ------- | ------ | ------------ | ----------------- |
| ad1 | 11 | 99 | prod1,prod2 |
| ad2 | 6 | 15 | prod2,prod3,prod4 |
But I want this(there is a difference in the product_sale column)
| ad_name | clicks | product_sale | products |
| ------- | ------ | ------------ | ----------------- |
| ad1 | 11 | 55 | prod1,prod2 |
| ad2 | 6 | 15 | prod2,prod3,prod4 |
54 because it counts only rows with id: 1,3,4 from products_insight because in these days ad with id 1 was active. (active means that there is a row in ads_insight table.
Related
In the link see my SQLFIDDLE and see b
CREATE TABLE Projects
(`p_id` int, `project_title` varchar(9), `l_id` int);
INSERT INTO Projects
(`p_id`, `project_title`, `l_id`)
VALUES
(1, 'A', 6),
(2, 'B', 6),
(3, 'C', 7),
(4, 'D', 8),
(5, 'E', 9),
(6, 'F', 10);
CREATE TABLE Locations
(`l_id` int, `title` varchar(9), `parent_id` int );
INSERT INTO Locations
(`l_id`, `title`, `parent_id`)
VALUES
(1, 'Country', 0),
(2, 'District1', 1),
(3, 'District2', 1),
(4, 'District3', 1),
(5, 'District4', 1),
(6, 'Loc 5', 2),
(7, 'Loc 6', 3),
(8, 'Loc 7', 3),
(9, 'Loc 8', 4),
(10, 'Loc 9', 4),
(11, 'Loc 10', 4),
(12, 'Loc 11', 5);
I would like to achieve this:
+------+-----------+-------------+
| L_ID | Title | count P_ID |
+------+-----------+-------------+
| 2 | District1 | 2 |
| 3 | District2 | 2 |
| 4 | District3 | 2 |
| 5 | District4 | 0 |
+----+------------+------+-------+
I have tried with INNER JOIN, LEFT OUTER JOIN. All i can achieve is like below and doesnt help me:
+------+-----------+----------------------+
| L_ID | Title | parent_id | counted |
+------+-----------+------------+---------+
| 6 | Loc 5 | 2 | 2 |
| 7 | Loc 6 | 3 | 2 |
| 9 | Loc 8 | 4 | 2 |
+---- -+-----------+------------+---------+
Locations table is a nested one, if this matters. I need to count projects that are in each District and also to get District name.
I tried:
SELECT l.*, COUNT(p.l_id) AS thecounted
FROM locations l
INNER JOIN projects p ON p.l_id = l.l_id
GROUP BY l.parent_id
and
SELECT l.*, COUNT(p.l_id) AS thecounted
FROM locations l
LEFT OUTER JOIN projects p on l.l_id = p.l_id
GROUP BY l.parent_id
Consider two joins:
select d.l_id, d.title, count(p.l_id) count_p_id
from locations d
left join locations l on l.parent_id = d.l_id
left join projects p on p.l_id = l.l_id
where d.parent_id = 0
group by d.l_id, d.title
The query starts from the list of districts (d), whose parent is 0. Then, it goes down one level to the locations (l), and looks up the corresponding projects (p). The final step is aggregation.
The solution of GMB returns this 1 row
l_id title count_p_id
1 Country 0
using this script version
select d.l_id, d.title, count(p.l_id) count_p_id
from locations d
left join locations l on l.parent_id = d.l_id
left join projects p on p.l_id = l.l_id
where d.parent_id = 0
group by d.l_id, d.title
We get the desired result with the slightly corrected condition
where d.parent_id = 1
Result:
l_id title count_p_id
2 District1 2
3 District2 2
4 District3 2
5 District4 0
Sorry for posting the answer, instead of a simple comment, which would be sufficient, but don't have enough reputation credits yet.
This is my database and fiddle setup:
CREATE TABLE PRODUCTS (
NAME varchar(20),
PRODUCT_ID int(3)
);
CREATE TABLE PAYMENT (
NAME varchar(20),
PAYMENT_ID int(4)
);
INSERT INTO PRODUCTS (NAME, PRODUCT_ID)
VALUES
("Apple", 1),
("Banana", 2),
("Watermelon", 3),
("Bread", 4),
("Milk", 5),
("Cake", 6),
("Candy", 7),
("Butter", 8),
("Carrot", 9),
("Tomato", 10);
INSERT INTO PAYMENT (NAME, PAYMENT_ID)
VALUES
("Banana", 85),
("Apple", 94),
("Banana", 94),
("Candy", 85),
("Banana", 105);
https://www.db-fiddle.com/f/5jsGYfWZ6pnzgf62Z5FA8L/0
I'm thinking I have to do some Count(CASE statement within here).
I saw something about pivots, but not sure about that?
Yes you are right, that is a possibility
I use IF when there is only one option.
SELECT
p.NAME
,SUM(IF(PAYMENT_ID = 85,1,0)) '85'
,SUM(IF(PAYMENT_ID = 94,1,0)) '94'
,SUM(IF(PAYMENT_ID = 105,1,0)) '105'
FROM PRODUCTS p LEFT JOIN PAYMENT pa ON p.NAME = pa.NAME
GROUP BY p.NAME;
There Reference between the two tables should be the id and not the name, the name maybe unique, but numbers need less space than text.
CREATE TABLE PRODUCTS (
NAME varchar(20),
PRODUCT_ID int(3)
);
CREATE TABLE PAYMENT (
NAME varchar(20),
PAYMENT_ID int(4)
);
INSERT INTO PRODUCTS (NAME, PRODUCT_ID)
VALUES
("Apple", 1),
("Banana", 2),
("Watermelon", 3),
("Bread", 4),
("Milk", 5),
("Cake", 6),
("Candy", 7),
("Butter", 8),
("Carrot", 9),
("Tomato", 10);
INSERT INTO PAYMENT (NAME, PAYMENT_ID)
VALUES
("Banana", 85),
("Apple", 94),
("Banana", 94),
("Candy", 85),
("Banana", 105);
✓
✓
✓
✓
SELECT
p.NAME
,SUM(IF(PAYMENT_ID = 85,1,0)) '85'
,SUM(IF(PAYMENT_ID = 94,1,0)) '94'
,SUM(IF(PAYMENT_ID = 105,1,0)) '105'
FROM PRODUCTS p LEFT JOIN PAYMENT pa ON p.NAME = pa.NAME
GROUP BY p.NAME;
NAME | 85 | 94 | 105
:--------- | -: | -: | --:
Apple | 0 | 1 | 0
Banana | 1 | 1 | 1
Bread | 0 | 0 | 0
Butter | 0 | 0 | 0
Cake | 0 | 0 | 0
Candy | 1 | 0 | 0
Carrot | 0 | 0 | 0
Milk | 0 | 0 | 0
Tomato | 0 | 0 | 0
Watermelon | 0 | 0 | 0
db<>fiddle here
MySQL has a convenient shortcut. I recommend:
SELECT p.NAME,
SUM(pa.PAYMENT_ID = 85) as payment_85,
SUM(pa.PAYMENT_ID = 94) as payment_94,
SUM(pa.PAYMENT_ID = 105) as payment_105
FROM PRODUCTS p LEFT JOIN
PAYMENT pa
ON p.NAME = pa.NAME
GROUP BY p.NAME;
Of course, if you don't care about products with no payments, you only need one table:
SELECT pa.NAME,
SUM(pa.PAYMENT_ID = 85) as payment_85,
SUM(pa.PAYMENT_ID = 94) as payment_94,
SUM(pa.PAYMENT_ID = 105) as payment_105
FROM PAYMENT pa
GROUP BY pa.NAME;
I have a table data as:
CREATE TABLE SERP (
id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
s_product_id INT,
search_product_result VARCHAR(255)
);
INSERT INTO SERP(s_product_id, search_product_result)
VALUES
(0, 'A'),
(0, 'B'),
(0, 'C'),
(0, 'D'),
(1, 'A'),
(1, 'E'),
(2, 'A'),
(2, 'B'),
(3, 'D'),
(3, 'E'),
(3, 'D');
The data set is as follows:
s_product_id | search_product_result
___________________________________________
0 | A
0 | B
0 | C
0 | D
-------------------------------------------
1 | A
1 | E
-------------------------------------------
2 | A
2 | B
-------------------------------------------
3 | D
3 | E
3 | D
I need to list all distinct search_product_result values and count frequencies of these values occurring in s_product_id.
Required Output result-set:
DISTINCT_SEARCH_PRODUCT | s_product_id_frequency_count
------------------------------------------------------------
A | 3
B | 2
C | 1
D | 2 [occurred twice in 3, but counted only once.]
E | 2
Here, A occurs in three s_product_id : 0, 1, 2, B in two : 0, 2, and so on.
D occurred twice in the same group 3, but is counted only once for that group.
I tried grouping by search_product_result, but this counts D twice in the same group.
select search_product_result, count(*) as Total from serp group by search_product_result
Output:
search_product_result | Total
------------------------------------
A | 3
B | 2
C | 1
D | 3 <---
B | 2
You can try below - use count(distinct s_product_id)
select search_product_result, count(distinct s_product_id) as Total
from serp group by search_product_result
use count(distinct()
select search_product_result, count(distinct s_product_id, search_product_result) as Total
from SERP
group by search_product_result
see dbfiddle
I am quiet new to programming so i need some help for the query in the below scenario.
user table
uid (PK)| name | score
------------------
1 | john | 20
2 | mary | 40
3 | david | 60
4 | nancy | 80
question_tb table
qid|question
-------------
1 | a
2 | b
3 | c
4 | d
question_user table
quid | user_id (FK) | question_id (FK)
--------------------------------
1 | 1 | 1
2 | 2 | 1
3 | 1 | 2
4 | 3 | 3
As above shows the table structure of the database. question_user table holds the questions that have been answered by a particular user. I want to get the list of questions in DESC manner that particular user has not been answered.
The following query should give you questions that user hasn't answered.
SELECT *
FROM question_tb as q
LEFT JOIN question_user as qu on q.qid = qu.question_id AND qu.user_id = USER_ID_HERE
WHERE qu.user_id IS NULL;
If you want to get the Questions not answered by particular user in DESC then use below query
SELECT * FROM questions WHERE qid NOT IN (SELECT question_id FROM `question_user` WHERE uid = USER-ID) ORDER BY qid DESC;
Try this:
Sample data:
create table users (uid int, name varchar(10), score int);
insert into users values
(1, 'john', 20),
(2, 'mary' , 40),
(3, 'david', 60),
(4, 'nancy', 80);
create table question_tb (qid int, question char(1));
insert into question_tb values
(1, 'a'),
(2, 'b'),
(3, 'c'),
(4, 'd');
create table question_user (quid int, user_id int, question_id int);
insert into question_user values
(1, 1, 1),
(2, 2, 1),
(3, 1, 2),
(4, 3, 3);
T-SQL:
select uid, qid from users u
cross join question_tb q
where not exists(
select 1 from question_user
where u.uid = user_id and q.qid = question_id)
order by uid, qid
I am trying to calculate the average of time for each part_id for the last 2 batches where the part_id is found and the average of time for each part_id for all the batches.
I have managed to isolate the Average of a part_id for the last 2 batches it was found but I can't integrated into the code so that it does the calculation for each part ID. I get an error Unknown column 'tst.part_id' in 'where clause', I need to pass that tst.part_id value to my nested select query.
Below is a fiddle I have:
http://sqlfiddle.com/#!9/77bea5/85
Query I am using:
SELECT tst.part_id,
AVG(tst.est_time) AS 'Average Time Overall',
(SELECT AVG(ft.avgLastMax)
FROM
(SELECT t2.avglst as 'avgLastMax', t2.numval as 'numval'
FROM (SELECT
#num:=CASE WHEN #last != tst3.batch_id
THEN #num:=(#num + 1)
ELSE #num:=#num END 'numval',
#last:=tst3.batch_id,
#name:=CASE WHEN #num > 2
THEN #name:=#name
ELSE #name:=(tst3.est_time) END 'avglst'
FROM test AS tst3,
( select #last:=0, #avg := 0, #name :=0 , #num :=0) var
/* GET AVERAGE FOR A SINGLE PART ID
WHERE tst3.part_id = 1 */
WHERE tst3.part_id = tst.part_id
ORDER BY tst3.run_id DESC) as t2
)
as ft
WHERE ft.numval <3) as 'AVG on last 2 batches'
FROM test AS tst
GROUP BY tst.part_id;
Here is what I am trying to get:
part_id AVG on last 2 batches Average Time Overall
1 27.25 25.67
2 22.5 22.5
3 16.67 16.67
4 47.5 47.5
Table Schema:
CREATE TABLE test
(`part_id` int, `est_time` int, `batch_id` int, `run_id` int, `line` varchar(1))
;
INSERT INTO test
(`part_id`, `est_time`, `batch_id`, `run_id`, `line`)
VALUES
(1, 20, 1, 1, 'T'),
(1, 25, 1, 2, 'T'),
(2, 30, 1, 3, 'T'),
(3, 15, 1, 4, 'T'),
(1, 10, 2, 5, 'X'),
(4, 40, 2, 8, 'X'),
(2, 15, 3, 9, 'T'),
(3, 15, 3, 10, 'T'),
(3, 20, 3, 11, 'T'),
(1, 34, 4, 12, 'X'),
(1, 32, 4, 13, 'X'),
(1, 33, 4, 14, 'X'),
(4, 55, 5, 15, 'T')
;
EDITED: Corrected the table and order by tst3.run_id DESC to get the last batch_id.
Assuming your first result is wrong...
SELECT a.part_id
, AVG(a.est_time)
FROM test a
JOIN
( SELECT x.part_id
, x.batch_id
FROM
( SELECT DISTINCT part_id
, batch_id
FROM test
) x
JOIN
( SELECT DISTINCT part_id
, batch_id
FROM test
) y
ON y.part_id = x.part_id
AND y.batch_id >= x.batch_id
GROUP
BY x.part_id
, x.batch_id
HAVING COUNT(*) <= 2
) b
ON b.part_id = a.part_id
AND b.batch_id = a.batch_id
GROUP
BY a.part_id;
+---------+-----------------+
| part_id | AVG(a.est_time) |
+---------+-----------------+
| 1 | 27.2500 |
| 2 | 22.5000 |
| 3 | 16.6667 |
| 4 | 47.5000 |
+---------+-----------------+
Or, faster... with variables...
SELECT part_id
, AVG(est_time) last_2_avg
FROM
( SELECT x.*
, CASE WHEN #part_id = part_id
THEN CASE WHEN #batch_id = batch_id THEN #i:=#i ELSE #i:=#i+1 END
ELSE #i:=1
END i
, #part_id := part_id
, #batch_id:= batch_id
FROM test x
, (SELECT #part_id := null, #batch_id:=null, #i:=1) vars
ORDER
BY part_id
, batch_id DESC
) a
WHERE a.i <= 2
GROUP
BY part_id;
+---------+------------+
| part_id | last_2_avg |
+---------+------------+
| 1 | 27.2500 |
| 2 | 22.5000 |
| 3 | 16.6667 |
| 4 | 47.5000 |
+---------+------------+
SELECT W.*,T1.ALLAVG
FROM (
SELECT V.PART_ID, AVG(V.EST_TIME) LAST2 FROM
(
SELECT U.PART_ID,U.MAXRN,X.RUN_ID,X.EST_TIME
FROM
(
/*LAST 2*/
SELECT S.PART_ID,MAX(S.RN) MAXRN
FROM
(
/*DERIVE A ROW NUMBER*/
SELECT T.PART_ID , T.RUN_ID,
IF (CONCAT(T.PART_ID,T.RUN_ID) <> #R ,#RN:=#RN+1,#RN:=1) RN,
#R:=CONCAT(T.PART_ID,T.RUN_ID) R
FROM (SELECT #RN:=0,#P:=0,#R:=0) RN, TEST T
ORDER BY T.PART_ID,T.RUN_ID
) S
GROUP BY S.PART_ID
) U
/*USING THE MAX ABOVE GET THE LAST 2 */
JOIN
(SELECT T.PART_ID , T.RUN_ID,T.EST_TIME,
IF (CONCAT(T.PART_ID,T.RUN_ID) <> #R1 ,#RN1:=#RN1+1,#RN1:=1) RN,
#R1:=CONCAT(T.PART_ID,T.RUN_ID) R
FROM (SELECT #RN1:=0,#P1:=0,#R1:=0) RN, TEST T
ORDER BY T.PART_ID,T.RUN_ID
) X ON X.PART_ID = U.PART_ID AND (X.RN BETWEEN U.MAXRN -1 AND U.MAXRN) #AMEND THIS FOR THE LAST N REQUIRED
) V
GROUP BY V.PART_ID
) W
JOIN
/*ALL*/
(SELECT T.PART_ID,AVG(T.EST_TIME) 'ALLAVG' FROM TEST T GROUP BY T.PART_ID) T1 ON T1.PART_ID = W.PART_ID
RESULT
+---------+---------+---------+
| PART_ID | LAST2 | ALLAVG |
+---------+---------+---------+
| 1 | 32.5000 | 25.6667 |
| 2 | 22.5000 | 22.5000 |
| 3 | 17.5000 | 16.6667 |
| 4 | 47.5000 | 47.5000 |
+---------+---------+---------+