Let's say, I have a table with the following columns:
date | event | user_id | unit_id |cost | ad_id | spend
03-15 | impression | 2353 | 3436 | 0.15 | NULL | NULL
03-15 | impression | 2353 | 3436 | 0.12 | NULL | NULL
03-15 | impression | 1234 | 5678 | 0.10 | NULL | NULL
03-15 | click | 1234 | 5678 | NULL | NULL | NULL
03-15 | create_ad | 1234 | 5678 | NULL | 6789 | 10
I want to calculate how many impressions on average it takes before a user creates an id.
In this particular scenario, it took one impression for user 1234 to create an ad.
I'm not sure that I can somehow use date to discriminate events (but logically all these events should happen at different moments). However, you can see that impressions have NULLs in ad_id and spend, while create_id does have a number in spend.
This one doesn't work:
select i.user_id
, i.unit_id
, count(i.event) impressions_n
, count(c.event) as ads_n
from add4ad i
left
join add4ad c
on i.user_id = c.user_id
and i.unit_id = c.unit_id
where i.event in ('impression')
and c.spend <> NULL
group
by i.user_id
, i.unit_id
I have created a SQLFiddle with this data
I went to SQL Fiddle and ran the test via MS SQL engine.
CREATE TABLE add4ad (date date, event varchar(10), user_id int,
unit_id int, cost float, ad_id float, spend float);
INSERT INTO add4ad (date, Event, user_id,unit_id,cost,ad_id,spend)
VALUES
('2018-03-15','impression','2353','3436','0.15',NULL,NULL),
('2018-03-15','impression','2353','3436','0.12',NULL,NULL),
('2018-03-15','impression','2353','3436','0.10',NULL,NULL),
('2018-03-15','click','1234','5678', NULL, NULL,NULL),
('2018-03-15','create_ad','2353','5678', NULL, 6789,10);
My query
with e10 as (select user_id, event, date, rowid=row_number() over (Partition by user_id order by date)
from add4ad
where event='create_ad'
),
e20 as ( -- get the first create_ad event
select user_id, date
from e10
where rowid=1
)
select a.user_id, count(1) as N
from e20 inner join add4ad a
on e20.user_id=a.user_id
and a.date<=e20.date
and a.event='impression'
group by a.user_id
If I got it right, you need to count distinct ads
CREATE TABLE add4ad (`date` date, `event` varchar(10), `user_id` int,
`unit_id` int, `cost` float, `ad_id` float, `spend` float);
INSERT INTO add4ad (`date`, `Event`, `user_id`,`unit_id`,`cost`,`ad_id`,`spend`)
VALUES
('2018-03-15','impression','2353','3436','0.15',NULL,NULL),
('2018-03-15','impression','2353','3436','0.12',NULL,NULL),
('2018-03-15','impression','2353','3436','0.10',NULL,NULL),
('2018-03-15','impression','1234','5678','0.10',NULL,NULL),
('2018-03-15','click','1234','5678', NULL, NULL,NULL),
('2018-03-15','create_ad','1234','5678', NULL, 6789,10),
('2018-03-16','impression','8765','8871','0.10',NULL,NULL),
('2018-03-16','impression','8765','8871','0.10',NULL,NULL),
('2018-03-16','impression','8765','8871','0.2',NULL,NULL),
('2018-03-16','impression','8765','8871','0.23',NULL,NULL),
('2018-03-16','click','8765','8871', NULL, NULL,NULL),
('2018-03-16','create_ad','8765','8871', NULL, 6789,10);
select i.user_id, i.unit_id, count(i.event) as impressions_n,
count(distinct c.event) as ads_n
from add4ad i
join add4ad c
on i.user_id = c.user_id and i.unit_id = c.unit_id
where i.event in ('impression')
and c.event in ('create_ad') and c.spend is not NULL
group by i.user_id, i.unit_id
Returns
user_id unit_id impressions_n ads_n
1234 5678 1 1
8765 8871 4 1
I've replaced left join with join because where as it is effectively makes your join inner If you still need left join move predicates to ON clause or handle NULLs in where.
fiddle
The issue is for checking NULLS you have to use is NULL or is not NULL. Also your data in fiddle is incorrect. It does not have impression for 1234 in fiddle.
select i.user_id, i.unit_id, count(i.event) as impressions_n,
count(c.event) as ads_n
from add4ad i
left join add4ad c
on i.user_id = c.user_id and i.unit_id = c.unit_id
where i.event in ('impression')
/*and c.event in ('create_ad')*/ and c.spend is not NULL
group by i.user_id, i.unit_id
Seems this is the solution:
select sum(c.impressions_n) / count(1) as average_num_of_impressions from (
select count(i.event) as impressions_n
from add4ad i
join add4ad c
on i.user_id = c.user_id and i.unit_id = c.unit_id
where i.event in ('impression') and c.event in ('create_ad')
group by i.user_id, i.unit_id ) c
Related
This is purely an efficiency question. I have two tables:
One where user IDs are stored based on their position within an entity
id | supervisor | manager | worker
-------+----------------+--------------+---------------
1 | 12 | 15 | 32
2 | 12 | 42 | 22
And the one with the user info:
id | name | email
-------+----------------+---------------------
12 | Bob | bob#example.com
15 | Dave | dave#example.com
Is it possible to do this with only one IN clause instead of 3?
SELECT `name`, `email`
FROM `tbl_users`
WHERE `id` IN
(SELECT `supervisor` FROM `tbl_warehouse` WHERE `id` = 'WAREHOUSEID')
OR `id` IN
(SELECT `manager` FROM `tbl_warehouse` WHERE `id` = 'WAREHOUSEID')
OR `id` IN
(SELECT `worker` FROM `tbl_warehouse` WHERE `id` = 'WAREHOUSEID')
I tried a few things trying to combine the supervisor, manager, worker columns into one column so the IN clause works but did so without success
You can use EXISTS and use the IN inside the subquery:
select `name`,
`email`
from `tbl_users` u
where exists (
select 1
from `tbl_warehouse` w
where w.id = 'WAREHOUSEID'
and u.id in (w.supervisor, w.manager, w.worker)
)
You achieve this using JOIN too:
select u.name, u.email
from tbl_users u
join tbl_warehouse w on u.id in (w.supervisor, w.manager, w.worker)
where w.id = 'WAREHOUSEID'
Or, also:
SELECT
name
, email
FROM tbl_users u
JOIN (
SELECT supervisor FROM tbl_warehouse WHERE id = 'WAREHOUSEID'
UNION ALL SELECT manager FROM tbl_warehouse WHERE id = 'WAREHOUSEID'
UNION ALL SELECT worker FROM tbl_warehouse WHERE id = 'WAREHOUSEID'
) w(id)
USING(id);
And make sure, as already said elsewhere, that you join INTEGERs with INTEGERs ...
I want to count all the results that have the same value in a mysql query but no matter what i have tried it does not give me the proper value
+-----+---------------+
| RoomType | ID |
+=====+===============+
| dining room | 2 |
+-----+---------------+
| sleeping room | 2 |
+-----+---------------+
| sleeping room | 2 |
+-----+---------------+
and i want to get the count of the sleeping rooms only.
Here is my query:
SELECT rt.RoomType, r.property_id AS ID
FROM Rooms r
INNER JOIN RoomTypes rt ON r.type = rt.id
WHERE r.property_id = '2'
I have also tried
SELECT SUM(IF(rt.RoomType = 'sleeping room', rt.RoomType, 0))
FROM Rooms r
INNER JOIN RoomTypes rt ON r.type = rt.id
WHERE r.property_id = '2'
but it does not give me the results i want. Any ideas?
I have found the answer and a working way like this:
SELECT SUM(rt.RoomType = 'sleeping room')
FROM Rooms r
INNER JOIN RoomTypes rt ON r.type = rt.id
WHERE r.property_id = '2'
and then it gives me the proper result.
Assuming a table like this:
CREATE TABLE `room_types` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`room_type` varchar(64) DEFAULT NULL,
`room_id` int(1) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=105 DEFAULT CHARSET=utf8'
and some data like this:
1245 dining room 2
1246 bath 3
1247 dining room 2
1248 kitchen 5
104 dining room 2
This should work:
select count(*), room_type, room_id from room_types where room_id = 2 group by room_type;
Result:
3 dining room 2
Hope it helps,
f.u.
Summary
I am looking for a semi-join(ish) query that selects a number of customers and joins their most recent data from other tables.
At a later time, I wish to directly append conditions to the end of the query: WHERE c.id IN (1,2,3)
Problem
As far as I am aware, my requirement rules out GROUP BY:
SELECT * FROM customer c
LEFT JOIN customer_address ca ON ca.customer_id = c.id
GROUP BY c.id
# PROBLEM: Cannot append conditions *after* GROUP BY!
With most subquery-based attempts, my problem is the same.
As an additional challenge, I cannot strictly use a semi-join, because I allow at least two types of phone numbers (mobile and landline), which come from the same table. As such, from the phone table I may be joining multiple records per customer, i.e. this is no longer a semi-join. My current solution below illustrates this.
Questions
The EXPLAIN result at the bottom looks performant to me. Am I correct? Are each of the subqueries executed only once? Update: It appears that DEPENDENT SUBQUERY is executed once for each row in the outer query. It would be great if we could avoid this.
Is there a better solution to what I am doing?
DDLs
DROP TABLE IF EXISTS customer;
CREATE TABLE `customer` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
);
DROP TABLE IF EXISTS customer_address;
CREATE TABLE `customer_address` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`customer_id` bigint(20) unsigned NOT NULL,
`street` varchar(85) DEFAULT NULL,
`house_number` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`)
);
DROP TABLE IF EXISTS customer_phone;
CREATE TABLE `customer_phone` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`customer_id` bigint(20) unsigned NOT NULL,
`phone` varchar(32) DEFAULT NULL,
`type` tinyint(3) unsigned NOT NULL COMMENT '1=mobile,2=landline',
PRIMARY KEY (`id`)
);
insert ignore customer values (1);
insert ignore customer_address values (1, 1, "OldStreet", 1),(2, 1, "NewStreet", 1);
insert ignore customer_phone values (1, 1, "12345-M", 1),(2, 1, "12345-L-Old", 2),(3, 1, "12345-L-New", 2);
SELECT * FROM customer;
+----+
| id |
+----+
| 1 |
+----+
SELECT * FROM customer_address;
+----+-------------+-----------+--------------+
| id | customer_id | street | house_number |
+----+-------------+-----------+--------------+
| 1 | 1 | OldStreet | 1 |
| 2 | 1 | NewStreet | 1 |
+----+-------------+-----------+--------------+
SELECT * FROM customer_phone;
+----+-------------+-------------+------+
| id | customer_id | phone | type |
+----+-------------+-------------+------+
| 1 | 1 | 12345-M | 1 |
| 2 | 1 | 12345-L-Old | 2 |
| 3 | 1 | 12345-L-New | 2 |
+----+-------------+-------------+------+
Solution so far
SELECT *
FROM customer c
# Join the most recent address
LEFT JOIN customer_address ca ON ca.id = (SELECT MAX(ca.id) FROM customer_address ca WHERE ca.customer_id = c.id)
# Join the most recent mobile phone number
LEFT JOIN customer_phone cphm ON cphm.id = (SELECT MAX(cphm.id) FROM customer_phone cphm WHERE cphm.customer_id = c.id AND cphm.`type` = 1)
# Join the most recent landline phone number
LEFT JOIN customer_phone cphl ON cphl.id = (SELECT MAX(cphl.id) FROM customer_phone cphl WHERE cphl.customer_id = c.id AND cphl.`type` = 2)
# Yay conditions appended at the end
WHERE c.id IN (1,2,3)
Fiddle
This fiddle gives the appropriate result set using the given solution. See my questions above.
http://sqlfiddle.com/#!9/98c57/3
I would avoid those dependent subqueries, instead try this:
SELECT
*
FROM customer c
LEFT JOIN (
SELECT
customer_id
, MAX(id) AS currid
FROM customer_phone
WHERE type = 1
GROUP BY
customer_id
) gm ON c.id = gm.customer_id
LEFT JOIN customer_phone mobis ON gm.currid = mobis.id
LEFT JOIN (
SELECT
customer_id
, MAX(id) AS currid
FROM customer_phone
WHERE type = 2
GROUP BY
customer_id
) gl ON c.id = gl.customer_id
LEFT JOIN customer_phone lands ON gl.currid = lands.id
WHERE c.id IN (1, 2, 3)
;
or, perhaps:
SELECT
*
FROM customer c
LEFT JOIN (
SELECT
customer_id
, MAX(case when type = 1 then id end) AS mobid
, MAX(case when type = 2 then id end) AS lndid
FROM customer_phone
GROUP BY
customer_id
) gp ON c.id = gp.customer_id
LEFT JOIN customer_phone mobis ON gp.mobid = mobis.id
LEFT JOIN customer_phone lands ON gp.lndid = lands.id
WHERE c.id IN (1, 2, 3)
;
see: http://sqlfiddle.com/#!9/ef983/1/
I have 3 tables.They are as follows.
1.system_user_master
________________________
user_id
user_type
user_registeration_code
user_first_name
user_last_name
2.book_appointment
-----------------------
booking_id
booking_date
puser_id
duser_id
doctor_name
3.routine_queue_patient
----------------------
queue_id
booking_id
queue_checkin_time
puser_id
duser_id
qdoctor_name
Now i want the result like
patient_registeration_code, patient_first_name, patient_last_name, booking_date, queue_checkin_time
In routine_queue_patient booking_id can be null.I want the list of patient of current date of selected doctor who are in routine_queue_patient with booking_id has some value or can be null.if booking_id is null then it shows in booking_date in query result is null and if booking id exists in routine_queue_patient then it display's the booking date.
I have written the query.The result is as follows.
booking_date | quecheck_in_time | user_first_name | user_last_name | user_regis_code
2013-11-12 | 2013-11-12 15:50:53 | rushang | patel | rp9898 |
2013-11-12 | 2013-11-12 16:00:11 | anjana | bhatt | ab9087
The booking_date of rushang patel must come in null as in the routine_queue_patient the booking_id of rushang patel is null but i got the booking_date of second record in front of rushang patel.
The query I have written is as follows.
SELECT DISTINCT b.booking_date
, r.queue_checkin_time
, s.user_first_name
, s.user_last_name
, s.user_registeration_code
FROM routine_queue_patient r
JOIN system_user_master s
ON r.puser_id = s.user_id
JOIN book_appointment b
ON ((b.booking_id = r.booking_id) OR r.booking_id is NULL)
AND DATE(r.queue_checkin_time) = '2013-11-12'
WHERE r.qdoctor_name = 'kashyup Nanavati'
AND DATE(b.booking_date) = '2013-11-12'
Thanks
Rushang
The join with book_appointment table is wrong. You shouldn't try to join on a null value.
Use left join to do it : it will join if a correspondant row is found, else all joined table columns will be null.
=>
SELECT DISTINCT b.booking_date
, r.queue_checkin_time
, s.user_first_name
, s.user_last_name
, s.user_registeration_code
FROM routine_queue_patient r
JOIN system_user_master s
ON r.puser_id = s.user_id
LEFT JOIN book_appointment b
ON (b.booking_id = r.booking_id AND DATE(b.booking_date) = '2013-11-12')
WHERE r.qdoctor_name = 'kashyup Nanavati'
AND DATE(r.queue_checkin_time) = '2013-11-12'
I have this freight.or_nos table which contains series of receipt numbers. I want to list all the or's being issued excluding the status='Cancelled' making the series broken in groups.
For example I have this receipt stab 125001-125050, and 125020 is cancelled so the listing result would be:
+-------------------------------------------------------+
| OR Start | OR End | Quantity | Amount |
+-------------------------------------------------------+
| 125001 | 125019 | 19 | |
+-------------------------------------------------------+
| 125021 | 125050 | 30 | |
+-------------------------------------------------------+
This seems to be a tough query.
Thanks for reading but I already made it, just now! :)
Here's my query(disregard the other characters it's form our CGI):
{.while SELECT `start`,`end`,or_prefix,or_suffix,SUM(a.amount) AS g_total,COUNT(*) AS qcount FROM (SELECT l.id AS `start`,( SELECT MIN(a.id) AS id FROM ( SELECT a.or_no AS id FROM freight.`or_nos` a WHERE a.status!='Cancelled' AND a.log_user = 0#user_teller AND DATE(a.or_date)='#user_date`DATE' AND IF(a.status='Default' AND a.amount=0,0,1) ) AS a LEFT OUTER JOIN ( SELECT a.or_no AS id FROM freight.`or_nos` a WHERE a.status!='Cancelled' AND a.log_user = 0#user_teller AND DATE(a.or_date)='#user_date`DATE' AND IF(a.status='Default' AND a.amount=0,0,1) ) AS b ON a.id = b.id - 1 WHERE b.id IS NULL AND a.id >= l.id ) AS `end` FROM ( SELECT a.or_no AS id FROM freight.`or_nos` a WHERE a.status!='Cancelled' AND a.log_user = 0#user_teller AND DATE(a.or_date)='#user_date`DATE' AND IF(a.status='Default' AND a.amount=0,0,1) ) AS l LEFT OUTER JOIN ( SELECT a.or_no AS id FROM freight.`or_nos` a WHERE a.log_user = 0#user_teller AND DATE(a.or_date)='#user_date`DATE' AND IF(a.status='Default' AND a.amount=0,0,1) ) AS r ON r.id = l.id - 1 WHERE r.id IS NULL) AS k LEFT JOIN freight.`or_nos` a ON a.`or_no` BETWEEN k.start AND k.end AND DATE(a.`or_date`)='#user_date`DATE' AND a.log_user =0#user_teller AND IF(a.status='Default' AND a.amount=0,0,1) AND a.status!='Cancelled' GROUP BY `start`}
{.start}{.x.24.12:end}{.x`p0.40.-5:qcount}{.x`p2.57.-15:g_total}{.asc 255}
{.wend}{.asc 255}