MYSQL: Select items that do not contain a certain date - mysql

Say I have a table
CustID | OrderDate
1 | 2017-05-30 05:15:18
2 | 2017-04-18 05:15:18
2 | 2017-04-15 05:15:18
3 | 2017-02-17 05:15:18
4 | 2017-05-29 05:15:18
4 | 2017-03-24 05:15:18
Any I only want to return back the CustIDs that do not contain an order date newer than 30 days (Today being 2017-05-30). So the above example would only return 2 and 3.
I have:
SELECT DISTINCT CustID
FROM TABLE
WHERE NOT EXISTS (SELECT CustID FROM TABLE WHERE OrderDate > DATE_ADD(NOW(),INTERVAL-30DAY));
But I only get syntax errors.
Thanks again, I am quite new with SQL.

You can try this:
select distinct CustIDs from YourTableName where OrderDate < now() - interval 30 day;
PS: In your query, you're using FROM TABLE - it isn't right, you must use FROM {YourTableName}, where {YourTableName} is real name of your table in database, like (customers, clients, etc.)

because you're missing a vital component in where clause: and t1.id = t2.id
select distinct CustID from table t1 where not exists (select 1 from table t2 where [whatever_your_conditions_are] and t1.id = t2.id);
A table similar to yours... It just has date instead of timestamp (and a relaxed date condition in where clause, just for the sake of simplicity):
selecting the users whose date > current_date (which is order_date in your case):

as far as i understand your question.
your query will be:
SELECT DISTINCT CustID FROM TABLE WHERE OrderDate < GETDATE()-30
Let me know if you require changes like < or > OrderDate in Where Clause.

Related

MySQL - Count unique users each day considering all previous days

I would like to count how many new unique users the database gets each day for all days recorded.
There will not be any duplicate ids per day, but there will be duplicates over multiple days.
If my table looks like this :
ID | DATE
---------
1 | 2022-05-21
1 | 2022-05-22
2 | 2022-05-22
1 | 2022-05-23
2 | 2022-05-23
1 | 2022-05-24
2 | 2022-05-24
3 | 2022-05-24
I would like the results to look like this :
DATE | NEW UNIQUE IDs
---------------------------
2022-05-21 | 1
2022-05-22 | 1
2022-05-23 | 0
2022-05-24 | 1
A query such as :
SELECT `date` , COUNT( DISTINCT id)
FROM tbl
GROUP BY DATE( `date` )
Will return the count per day and will not take into account previous days.
Any assistance would be appreciated.
Edit : Using MySQL 8
The user is new when the date is the least date for this user.
So you need in something like
SELECT date, COUNT(new_users.id)
FROM calendar
LEFT JOIN ( SELECT id, MIN(date) date
FROM test
GROUP BY id ) new_users USING (date)
GROUP BY date
calendar is either static or dynamically generated table with needed dates list. It can be even SELECT DISTINCT date FROM test subquery.
Start with a subquery showing the earliest date where each id appears.
SELECT MIN(`date`) `firstdate`, id
FROM tbl
GROUP BY id
Then do your count on that subquery. here.
SELECT firstdate, COUNT(*)
FROM (
SELECT MIN(`date`) `firstdate`, id
FROM tbl
GROUP BY id
) m
GROUP BY firstdate
That gives you what you want.
But it doesn't have rows for the dates where no new user ids first appeared.
Only count (and sum) the rows where the left join fails:
SELECT
m1.`DATE` ,
sum(CASE WHEN m2.id is null THEN 1 ELSE 0 END) as C
FROM mytable m1
LEFT JOIN mytable m2 ON m2.`DATE`<m1.`DATE` AND m2.ID=m1.ID
GROUP BY m1.`DATE`
see: DBFIDDLE

SQL get max of columns where a row equals something

If I have Table with 3-columns:
Date | Name | Num
oct1 | Bob | 2
oct2 | Zayne | 1
oct1 | Test | 5
oct2 | Apple | 7
I want to retrieve the rows where Num is MAX,
WHERE Date = oct1 or Date = oct2
So I want result to be:
oct1 Test 5
oct2 Apple 7
MYSQL is preferred. But SQL answer be given also. Thanks.
You can try below using correlated subquery
select * from tablename a
where num in (select max(num) from tablename b where a.date=b.date)
and date in ('oct1', 'oct2')
It sounds like you want this query:
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT Date, MAX(Num) AS max_num
FROM yourTable
WHERE Date IN ('oct1', 'oct2')
GROUP BY Date
) t2
ON t1.Date = t2.Date AND t1.Num = t2.max_num
WHERE t1.Date IN ('oct1', 'oct2');
By the way, you should seriously consider storing proper date data in an actual date or datetime column in MySQL. It appears you are just storing text right now, which would be hard to work with.
You can try to use correctly subquery
Schema (MySQL v5.7)
CREATE TABLE T(
Date VARCHAR(50),
Name VARCHAR(50),
Num INT
);
INSERT INTO T VALUES ('oct1','Bob',2);
INSERT INTO T VALUES ('oct2','Zayne',1);
INSERT INTO T VALUES ('oct1','Test',5);
INSERT INTO T VALUES ('oct2','Apple',7);
Query #1
SELECT *
FROM T t1
WHERE Num = (SELECT MAX(Num) FROM T tt WHERE t1.Date = tt.Date)
AND
t1.Date in ('oct1','oct2')
| Date | Name | Num |
| ---- | ----- | --- |
| oct1 | Test | 5 |
| oct2 | Apple | 7 |
View on DB Fiddle
As you where asking for a standard way to do this: All the answers given so far comply with the SQL standard. One more possible approach in standard SQL is to use a window function. This is only featured in MySQL as of version 8, however.
select date, name, num
from
(
select date, name, num, max(num) over (partition by date) as max_num
from mytable
) analyzed
where num = maxnum
order by date;
This only reads the table once, which can (but not necessarily does) speed up the query.
You can use corelated subquery just like below
SELECT *
FROM T t1
WHERE Num = (SELECT MAX(Num) FROM T t2 WHERE t2.Date = t1.Date)
Fiddle link
Date Name Num
oct1 Test 5
oct2 Apple 7

MySQL select rows where given date lies between the dates stored in table

Suppose I have some data like:
id status activity_date
--- ------ -------------
101 R 2014-01-12
101 Mt 2014-04-27
101 R 2014-05-18
102 R 2014-02-19
Note that for rows with id = 101 we have activity between 2014-01-12 to 2014-04-26 and 2014-05-18 to current date.
Now I need to select that data where status = 'R' and the date is the most current date as of a given date, e.g. if I search for 2014-02-02, I would find the status row created on 2014-01-12, because that was the status that was still valid at the time for entity ID 101.
If I understand correctly:
Step 1: Convert the start and end date rows into columns. For this, you must join the table with itself based on this criteria:
SELECT
dates_fr.id,
dates_fr.activity_date AS date_fr,
MIN(dates_to.activity_date) AS date_to
FROM test AS dates_fr
LEFT JOIN test AS dates_to ON
dates_to.id = dates_fr.id AND
dates_to.status = 'Mt' AND
dates_to.activity_date > dates_fr.activity_date
WHERE dates_fr.status = 'R'
GROUP BY dates_fr.id, dates_fr.activity_date
+------+------------+------------+
| id | date_fr | date_to |
+------+------------+------------+
| 101 | 2014-01-12 | 2014-04-27 |
| 101 | 2014-05-18 | NULL |
| 102 | 2014-02-19 | NULL |
+------+------------+------------+
Step 2: The rest is simple. Wrap the query inside another query and use appropriate where clause:
SELECT * FROM (
SELECT
dates_fr.id,
dates_fr.activity_date AS date_fr,
MIN(dates_to.activity_date) AS date_to
FROM test AS dates_fr
LEFT JOIN test AS dates_to ON
dates_to.id = dates_fr.id AND
dates_to.status = 'Mt' AND
dates_to.activity_date > dates_fr.activity_date
WHERE dates_fr.status = 'R'
GROUP BY dates_fr.id, dates_fr.activity_date
) AS temp WHERE '2014-02-02' >= temp.date_fr and ('2014-02-02' < temp.date_to OR temp.date_to IS NULL)
+------+------------+------------+
| id | date_fr | date_to |
+------+------------+------------+
| 101 | 2014-01-12 | 2014-04-27 |
+------+------------+------------+
SQL Fiddle
You can try
select id, status, activity_date
from TABLE
where status = "R" and activity_date = "2014-02-02"
where TABLE is name of your table
I think you need following ans
SELECT id,MAX(CAST(ACTIVITY_DATE AS date),MIN(CAST (ACTIVITY_DATE AS date)
FROM Table_Name WHERE CAST('2014-02-02' AS date)
BETWEEN MIN(CAST (ACTIVITY_DATE AS date) AND MAX(CAST(ACTIVITY_DATE AS date)
AND Status='R'
GROUP BY id
Try this:
select * from yourtable
where status='R' and activity_date= '2014-02-02'
You can make a query to effectively give you the most status as of a date, e.g.
SELECT
id,
substr(max(concat(activity_date, status)),11) as status,
max(activity_date) as activity_date
FROM table
WHERE activity_date <= '2014-02-02'
GROUP by id;
Then, similar to Salman's answer, you can use this result inside another query and look for all those results with a status of 'R'
SELECT * from (
SELECT
id,
substr(max(concat(activity_date, status)),11) as status,
max(activity_date) as activity_date
FROM table
WHERE activity_date <= '2014-02-02'
GROUP by id
) AS temp WHERE temp.status = 'R';
Edit: Rather than use the questionable method of sorting the statuses, you could identify the relevant maximum record with a sub-query, so the original query would become
SELECT join1.* FROM table AS join1
INNER JOIN (
SELECT id, max(activity_date) as max_activity_date
FROM table
WHERE activity_date < '2014-02-02'
GROUP BY id
) AS join2
ON join1.id = join2.id AND join1.activity_date = join2.max_activity_date;
and the full query
SELECT * from (
SELECT join1.* FROM table AS join1
INNER JOIN (
SELECT id, max(activity_date) as max_activity_date
FROM table
WHERE activity_date < '2014-02-02'
GROUP BY id
) AS join2
ON join1.id = join2.id AND join1.activity_date = join2.max_activity_date
) AS temp WHERE temp.status = 'R';
try the following
SELECT *
FROM your_relation
WHERE status='R'
AND activity_data="2014-02-02"
I completely agree with Salman's response, the table could be designed in a fashion that allows for greater query accuracy and extensibility. However, the question asked, with regards to a query selecting information based on status and date range can be expressed as.
SELECT * FROM Table_1
WHERE ((status = 'R')
AND ((activity_date BETWEEN '2014-01-12' AND '2014-04-26')
OR activity_date > CONVERT(DATETIME, '2014-05-17')))
This will select all data with a status of 'R' and will use the BETWEEN operator for the range desired; moreover, the conversion of the final operator is because the expression is evaluated as a mathematical expression and requires explicit conversion.

Mysql each row sum

How can I get result like below with mysql?
> +--------+------+------------+
> | code | qty | total |
> +--------+------+------------+
> | aaa | 30 | 75 |
> | bbb | 20 | 45 |
> | ccc | 25 | 25 |
> +--------+------+------------+
total is value of the rows and the others that comes after this.
You can do this with a correlated subquery -- assuming that the ordering is alphabetical:
select code, qty,
(select sum(t2.qty)
from mytable t2
where t2.code >= t.code
) as total
from mytable t;
SQL tables represent unordered sets. So, a table, by itself, has no notion of rows coming after. In your example, the codes are alphabetical, so they provide one definition. In practice, there is usually an id or creation date that serves this purpose.
I would use join, imho usually fits better.
Data:
create table tab (
code varchar(10),
qty int
);
insert into tab (code, qty)
select * from (
select 'aaa' as code, 30 as qty union
select 'bbb', 20 union
select 'ccc', 25
) t
Query:
select t.code, t.qty, sum(t1.qty) as total
from tab t
join tab t1 on t.code <= t1.code
group by t.code, t.qty
order by t.code
The best way is to try both queries (my and with subquery that #Gordon mentioned) and choose the faster one.
Fiddle: http://sqlfiddle.com/#!2/24c0f/1
Consider using variables. It looks like:
select code, qty, (#total := ifnull(#total, 0) + qty) as total
from your_table
order by code desc
...and reverse query results list afterward.
If you need pure SQL solution, you may compute sum of all your qty values and store it in variable.
Also, look at: Calculate a running total in MySQL

Using ORDER BY and GROUP BY together

My table looks like this (and I'm using MySQL):
m_id | v_id | timestamp
------------------------
6 | 1 | 1333635317
34 | 1 | 1333635323
34 | 1 | 1333635336
6 | 1 | 1333635343
6 | 1 | 1333635349
My target is to take each m_id one time, and order by the highest timestamp.
The result should be:
m_id | v_id | timestamp
------------------------
6 | 1 | 1333635349
34 | 1 | 1333635336
And i wrote this query:
SELECT * FROM table GROUP BY m_id ORDER BY timestamp DESC
But, the results are:
m_id | v_id | timestamp
------------------------
34 | 1 | 1333635323
6 | 1 | 1333635317
I think it causes because it first does GROUP_BY and then ORDER the results.
Any ideas? Thank you.
One way to do this that correctly uses group by:
select l.*
from table l
inner join (
select
m_id, max(timestamp) as latest
from table
group by m_id
) r
on l.timestamp = r.latest and l.m_id = r.m_id
order by timestamp desc
How this works:
selects the latest timestamp for each distinct m_id in the subquery
only selects rows from table that match a row from the subquery (this operation -- where a join is performed, but no columns are selected from the second table, it's just used as a filter -- is known as a "semijoin" in case you were curious)
orders the rows
If you really don't care about which timestamp you'll get and your v_id is always the same for a given m_i you can do the following:
select m_id, v_id, max(timestamp) from table
group by m_id, v_id
order by max(timestamp) desc
Now, if the v_id changes for a given m_id then you should do the following
select t1.* from table t1
left join table t2 on t1.m_id = t2.m_id and t1.timestamp < t2.timestamp
where t2.timestamp is null
order by t1.timestamp desc
Here is the simplest solution
select m_id,v_id,max(timestamp) from table group by m_id;
Group by m_id but get max of timestamp for each m_id.
You can try this
SELECT tbl.* FROM (SELECT * FROM table ORDER BY timestamp DESC) as tbl
GROUP BY tbl.m_id
SQL>
SELECT interview.qtrcode QTR, interview.companyname "Company Name", interview.division Division
FROM interview
JOIN jobsdev.employer
ON (interview.companyname = employer.companyname AND employer.zipcode like '100%')
GROUP BY interview.qtrcode, interview.companyname, interview.division
ORDER BY interview.qtrcode;
I felt confused when I tried to understand the question and answers at first. I spent some time reading and I would like to make a summary.
The OP's example is a little bit misleading.
At first I didn't understand why the accepted answer is the accepted answer.. I thought that the OP's request could be simply fulfilled with
select m_id, v_id, max(timestamp) as max_time from table
group by m_id, v_id
order by max_time desc
Then I took a second look at the accepted answer. And I found that actually the OP wants to express that, for a sample table like:
m_id | v_id | timestamp
------------------------
6 | 1 | 11
34 | 2 | 12
34 | 3 | 13
6 | 4 | 14
6 | 5 | 15
he wants to select all columns based only on (group by)m_id and (order by)timestamp.
Then the above sql won't work. If you still don't get it, imagine you have more columns than m_id | v_id | timestamp, e.g m_id | v_id | timestamp| columnA | columnB |column C| .... With group by, you can only select those "group by" columns and aggreate functions in the result.
By far, you should have understood the accepted answer.
What's more, check row_number function introduced in MySQL 8.0:
https://www.mysqltutorial.org/mysql-window-functions/mysql-row_number-function/
Finding top N rows of every group
It does the simlar thing as the accepted answer.
Some answers are wrong. My MySQL gives me error.
select m_id,v_id,max(timestamp) from table group by m_id;
#abinash sahoo
SELECT m_id,v_id,MAX(TIMESTAMP) AS TIME
FROM table_name
GROUP BY m_id
#Vikas Garhwal
Error message:
[42000][1055] Expression #2 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'testdb.test_table.v_id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
Why make it so complicated? This worked.
SELECT m_id,v_id,MAX(TIMESTAMP) AS TIME
FROM table_name
GROUP BY m_id
Just you need to desc with asc. Write the query like below. It will return the values in ascending order.
SELECT * FROM table GROUP BY m_id ORDER BY m_id asc;