Ask about Query in MySQL - mysql

I have table like this:
CreateDate | UserID
2012-01-1 | 1
2012-01-10 | 2
2012-01-20 | 3
2012-02-2 | 4
2012-02-11 | 1
2012-02-22 | 2
2012-03-5 | 3
2012-03-13 | 4
2012-03-17 | 5
I need the query to show UserID which created after 1 February 2013 and not exist in database befor 1 February 2013
From the above example the result must be:
CreateDate | UserID
2012-02-2 | 4
2012-03-13 | 4
2012-03-17 | 5
Can it resolved only in single query without Stored Procedure?

You can use a subquery which separately gets the UserID which exist before Feb. 2, 2013 and the result of the subquery is then joined back on the table itself using LEFT JOIN.
SELECT a.*
FROM tableName a
LEFT JOIN
(
SELECT UserID
FROM tableName
WHERE CreateDate < '2013-02-01'
) b ON a.userID = b.userID
WHERE a.CreateDate > '2013-02-01' AND
b.userID IS NULL
SQLFiddle Demo
for faster performance, add an INDEX on column userID.
SQL JOIN vs IN performance?

This is one way to do it:
select
CreateDate,
UserID
from Users
where CreateDate >= '2012-02-01'
and UserId not in (
select UserId
from Users
where CreateDate < '2012-02-01'
)
SqlFiddle link: http://www.sqlfiddle.com/#!2/7efae/2

Related

mysql counts the number of new visitors by day

MySQL version: 5.7
Here is users table:
+------------+------+
| date | uid |
+------------+------+
| 2020-06-29 05:00:00 | 352 |
| 2020-06-29 08:00:00 | 354 |
| 2020-06-29 09:25:53 | 354 |
| 2020-06-30 08:00:00 | 863 |
| 2020-06-30 09:00:01 | 352 |
| 2020-06-30 09:59:59 | 352 |
| 2020-07-01 07:00:00 | 358 |
| 2020-07-01 09:00:00 | 358 |
+------------+------+
I want to count the number of new visitors per day,But there is an important condition here that new visitors of the day cannot be visited before.
I want the result:
Result:
+------------+------------------+
| date | new_user_count |
+------------+------------------+
| 2020-06-29 | 2 |
| 2020-06-30 | 1 |
| 2020-07-01 | 1 |
+------------+------------------+
The above result is equivalent to these three sql:
2020-06-29 (352,354) : select count( distinct uid ) as new_user_count from users where DATE(date) = '2020-06-29' and uid not in ( select distinct uid from users where date < '2020-06-29 05:00:00'); #2
2020-06-30 (863): select count( distinct uid ) as new_user_count from users where DATE(date)= '2020-06-30' and uid not in ( select distinct uid from users where date < '2020-06-30 08:00:00'); # 1
2020-07-01 (358): select count( distinct uid ) as new_user_count from users where DATE(date)= '2020-07-01' and uid not in ( select distinct uid from users where date < '2020-07-01 07:00:00'); # 1
I haven't thought of it until now, thanks
Here is Online users table
You could try using a correlated subquery to check if each user visit be the first or not:
SELECT
date,
SUM(CASE WHEN NOT EXISTS (SELECT 1 FROM users u2
WHERE u2.date < u1.date AND u2.uid = u1.uid)
THEN 1 ELSE 0 END) AS new_user_count
FROM
(SELECT DISTINCT date, uid FROM users) u1
GROUP BY
date;
Demo
The above logic actually reads straightforward, and says to count a user record only if we cannot find that same user appearing in the table at some later date. Note that I use distinct selects, because it appears that in your data a given user might appear more than once on the same date. This data would spoof the above correlated subquery, so we ensure that a given user appear only once on a given date (and besides, one user can only be counted once per day anyway).
SELECT
date,
(
SELECT COUNT(DISTINCT u1.uid)
FROM users u1
WHERE NOT EXISTS(
SELECT * FROM users u2
WHERE u2.uid = u1.uid AND u2.date < u0.date
) AND u1.date = u0.date
)
FROM
users u0
GROUP BY
date
;
-- get date and the amount of distinct users
SELECT date, COUNT(DISTINCT uid)
-- from users table
FROM users
-- only when there not exists a row
WHERE NOT EXISTS ( SELECT NULL -- may use any literal value instead of NULL
-- in the table
FROM users u
-- with this user id
WHERE users.uid = u.uid
-- but earlier (less) date
AND users.date > u.date )
GROUP BY date;

MySQL select count only new id's for each year

I have a MySQL table that looks like this
id | client_id | date
--------------------------------------
1 | 12 | 02/02/2008
2 | 15 | 12/06/2008
3 | 23 | 11/12/2008
4 | 12 | 18/01/2009
5 | 12 | 03/03/2009
6 | 18 | 02/07/2009
7 | 23 | 08/09/2010
8 | 18 | 02/10/2010
9 | 21 | 30/11/2010
What I am trying to do is get the number of new clients for each year. 2008 has 3 new clients(12,15,23), 2009 has 1 new client(18) and 2010 has 1 new client(21).
So far I have this query that gives me the distinct clients for each year, that is 3 for 2008, 2 for 2009 and 3 for 2010.
SELECT COUNT(DISTINCT client_id) FROM table GROUP BY YEAR(date)
Any help would be appreciated..
You could use a subquery to get the first year of every client_id grouped by client_id, and then count the occurrence of client_id grouped by year, so:
SELECT COUNT(client_id), YEAR_MIN FROM (
SELECT client_id, MIN(YEAR(date)) AS YEAR_MIN
FROM table
GROUP BY client_id) AS T
GROUP BY YEAR_MIN
SQL Fiddle here
So you want to count the first date a client appears in the table. In other words, the row for which no other row exists with an earlier date and the same client. You can do this with an exclusion join.
Then you can count them per year as you're doing now.
SELECT YEAR(t.date) AS yr, COUNT(t.client_id) AS client_count
FROM (
SELECT t1.client_id, t1.date
FROM mytable AS t1
LEFT JOIN mytable AS t2 ON (t1.client_id=t2.client_id AND t1.date > t2.date)
WHERE t2.client_id IS NULL) AS t
GROUP BY yr
You should store dates using the DATE data type, which uses YYYY-MM-DD format. You won't be able to do > comparisons if your dates are stored as strings in DD-MM-YYYY format.
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id SERIAL PRIMARY KEY
,client_id INT NOT NULL
,date INT NOT NULL
);
INSERT INTO my_table VALUES
(1,12,2008),
(2,15,2008),
(3,23,2008),
(4,12,2009),
(5,12,2009),
(6,18,2009),
(7,23,2010),
(8,18,2010),
(9,21,2010);
SELECT year
, COUNT(*) total
FROM
( SELECT client_id, MIN(date) year FROM my_table GROUP BY client_id ) x
GROUP
BY year;
+------+-------+
| year | total |
+------+-------+
| 2008 | 3 |
| 2009 | 1 |
| 2010 | 1 |
+------+-------+

Select all rows with multiple occurrences - on same day

I have a single MySQL table with the name 'checkins' and 4 columns.
id | userIDFK | checkin_datetime | shopId
------------------------------------------------
1 | 1 | 2018-01-18 09:44:00 | 3
2 | 2 | 2018-01-18 10:32:00 | 3
3 | 3 | 2018-01-18 11:19:00 | 3
4 | 1 | 2018-01-18 17:57:00 | 3
5 | 1 | 2018-01-18 16:31:00 | 1
6 | 1 | 2018-01-19 08:31:00 | 3
Basically I want to find rows where users have checked-in more than once (>=2) on the same day and the same shop. So for instance if a user checks-in as in rows with ids 1 and 4 (same user, same day, same shop), the query should return a hit with the the entire rows (id, userIDFK, checkin_datetime, shopId). Hope this makes sense.
I already tried using
SELECT id, userIDFK, checkin_datetime, shopId
FROM (
SELECT * FROM 'checkins' WHERE COUNT(userIDFK)>=2 AND COUNT(shopId)>=2
)
The same day part I have no clew how to do it, and I know this query is way off, but this is the best I could.
You can try grouping by userId checkin_date and shopID
SELECT userIDFK, checkin_datetime, shopId,COUNT(SHOPiD)
FROM checkins
GROUP BY userIDFK, DATE(checkin_datetime), shopId
HAVING COUNT(SHOPID)>1
EDIT
You can include a subquery to get all lines:
select b.id,b.userIDFK, b.checkin_datetime, b.shopId
from checkins b
where (SELECT COUNT(SHOPiD)
FROM checkins a
where a.userIDFK=b.userIDFK and date(a.checkin_datetime)=date(b.checkin_datetime) and a.shopId=b.a.shopId
GROUP BY userIDFK, DATE(checkin_datetime), shopId)>1
GROUPBY can be used to get the multiple occurrences.
SELECT id, userIDFK, checkin_datetime, shopId
FROM checkins
GROUP BY userIDFK, DATE(checkin_datetime), shopId
HAVING count(id) > 1;
Hope it helps!
EDIT:
Using inner join you can achieve it. Here is the query:
SELECT c1.* FROM checkins c1 INNER JOIN checkins c2
ON c1.userIDFK = c2.userIDFK
AND date(c1.checkin_datetime) = date(c2.checkin_datetime)
AND c1.shopId = c2.shopId
AND c1.id != c2.id
Cheers!!

Update with SUM and LIMIT, rolling SUM

I have 2 tables, SVISE and OVERW
Inside OVERW I have some scores with person ids and the date of that score.
E.g
p_id degrees mo_date
5 10.2 2013-10-09
5 9.85 2013-03-10
8 14.75 2013-04-25
8 11.00 2013-02-22
5 5.45 2013-08-11
5 6.2 2013-06-10
SVISE.ofh field must be updated with the sum of the last three records
(for a specific person, ordered by date descending), so for person with id 5, the sum would result from the rows
5 10.2 2013-10-09
5 5.45 2013-08-11
5 6.2 2013-06-10
sum=21.85.
Desired final result on SVISE, based on the values above:
HID OFH START
5 21.85 October, 16 2013 ##(10.2 + 5.45 + 6.2)
5 21.5 September, 07 2013 ##(5.45 + 6.2 + 9.85)
5 0 March, 05 2013 ##(no rows)
8 25.75 October, 14 2013 ##(14.75 + 11)
3 0 October, 14 2013 ##(no rows)
5 0 March, 05 2012 ##(no rows)
OFHwas 0 initially
I can get the total sum for a specific person, but I can't use limit to get the last 3 rows. It gets ignored.
This is the query I use to retrieve the sum of all degrees per person for a given date:
UPDATE SVISE SV
SET
SV.ofh=(SELECT sum(degrees) FROM OVERW WHERE p_id =SV.hid
AND date(mo_date)<date(SV.start)
AND year(mo_date)=year(SV.start))
I cannot just use limit with sum:
UPDATE SVISE SV
SET
SV.ofh=(SELECT sum(degrees) FROM OVERW WHERE p_id =SV.hid
AND date(mo_date)<date(SV.start)
AND year(mo_date)=year(SV.start)
ORDER BY mo_date DESC
LIMIT 3)
This does not work.
I have tried with multi-table updates and with nested queries to achieve this.
Every scenario has known limitations that block me from accomplishing the desired result.
Nested queries cant see the parent table. Unknown column 'SV.hid'in 'where clause'
Multi-table update cant be use with limit. Incorrect usage of UPDATE and LIMIT
Any solution will do. There is no need to do it in a single query. If anyone wants to try even with an intermediate table.
An SQL fiddle is also available.
Thanks in advance for your help.
--Update--
Here is the solution from Akash: http://sqlfiddle.com/#!2/4cf1a/1
This should work,
UPDATED to have a join on svice
UPDATE
svice SV
JOIN (
SELECT
hid,
start,
sum(degrees) as degrees
FROM
(
SELECT
*,
IF(#prev_row <> unix_timestamp(start)+P_ID, #row_number:=0,NULL),
#prev_row:=unix_timestamp(start)+P_ID,
#row_number:=#row_number+1 as row_number
FROM
(
SELECT
mo_date,
p_id,
hid,
start,
degrees
FROM
OVERW
JOIN svice sv ON ( p_id = hid
AND date(mo_date)<date(SV.start)
AND year(mo_date)=year(SV.start) )
ORDER BY
hid,
start,
mo_date desc
) sub_query1
JOIN ( select #row_number:=0, #prev_row:=0 ) sub_query2
) sub_query
where
row_number <= 3
GROUP BY
hid,
start
) sub_query ON ( sub_query.hid = sv.hid AND sub_query.start = sv.start )
SET
SV.ofh = sub_query.degrees
Note: Check this with your updated data, the test data provided could not yield the results you expected due to the date conditions
Try
UPDATE svice SV
JOIN (SELECT SUM(degrees)sumdeg,p_id FROM(SELECT DISTINCT degrees,p_id FROM OVERW,svice WHERE OVERW.p_id IN (SELECT svice.hid FROM svice)
AND date(mo_date)<date(svice.start)
AND year(mo_date)=year(svice.start)ORDER BY mo_date DESC )deg group by p_id)bbc
ON bbc.p_id=SV.hid
SET
SV.ofh=bbc.sumdeg where p_id =SV.hid
http://sqlfiddle.com/#!2/95b42/42
Getting closer,now it "only" needs a limit in GROUP BY.
Two assumptions:
You can figure out how to turn this into an update, and
A PK exists on (id,mo_date)
Then you can do this -
SELECT p_id
, SUM(degrees) ttl
FROM
( SELECT x.*
FROM overw x
JOIN overw y
ON y.p_id = x.p_id
AND y.mo_date >= x.mo_date
GROUP
BY p_id
, mo_date HAVING COUNT(*) <= 3
) a
GROUP
BY p_id;
Maybe I'm slow, but let's ignore svice for now.
Can you show the correct result and the working for each row below...
+------+---------+------------+--------+
| p_id | degrees | mo_date | result |
+------+---------+------------+--------+
| 5 | 6.20 | 2013-06-10 | ? |
| 5 | 5.45 | 2013-08-11 | ? |
| 5 | 10.20 | 2013-10-09 | 21.85 | <- = 10.2+5.45+6.2 = 21.85
| 8 | 14.75 | 2013-04-25 | ? |
| 5 | 9.85 | 2013-03-10 | ? |
| 8 | 11.00 | 2013-02-22 | ? |
+------+---------+------------+--------+

Selecting non-group field from query with aggregate function

I have a table tbl with three columns:
id | fk | dateof
1 | 1 | 2012-01-01
2 | 1 | 2012-01-02
3 | 2 | 2012-02-01
4 | 2 | 2012-03-01
5 | 3 | 2012-04-01
id is the ID of the row, fk is a foreign key to another table and dateof is a date column.
What I want is to get the id where the dateof is the latest grouped by fk. What I've tried:
SELECT id, MAX(dateof) FROM tbl GROUP BY fk
But I get results like this:
1 | 2012-01-02
3 | 2012-03-01
5 | 2012-04-01
When I want:
2 | 2012-01-02
4 | 2012-03-01
5 | 2012-04-01
How can I query and get the results I'm looking for? MySQL server if it matters. Thanks.
Personally I would do
SELECT id, tbl.dateof dateof
FROM tbl
INNER JOIN
(SELECT fk, MAX(dateof) dateof
FROM tbl
GROUP BY fk) temp
ON tbl.fk = temp.fk AND tbl.dateof = temp.dateof
Gordon's answer is correct and less code, but I prefer creating a temp table. It's more clear to other developers what I'm doing.
To get what you want:
select t.*
from (select tbl.*,
row_number() over (partition by fk order by dateof desc) as seqnum
from tbl
) t
where seqnum = 1
This assumes that you are using a reasonable database that has window functions. You don't specify the database in your question.