Find rows with consecutive dates - mysql

Is it possible to know which customer came at the bar 3 consecutive days? (john in my case)
Thanks in advance
Name
Age
Date
Amount
Paul
12
2021-12-01
20
John
19
2021-12-01
10
Jack
17
2021-13-01
7
John
19
2021-13-01
8
John
19
2021-14-01
17

so I would approach this by
SELECT FROM Table
LEFT JOIN Table As PrevDay on PrevDay.Customer = Table.Customer
AND PrevDay.date = DATEADD(DAY,-1,Table.Date)
LEFT JOIN Table AS NextDay on NextDay,Customer = Table.Customer
AND NextDay.Date = DATEADD(DATE,1,Table.Date)
WHERE PrevDay.Customer is not NULL
AND NextDay.Customer is not null

Assuming that the data type of the column Date is DATE you can use a self join and aggregation:
SELECT DISTINCT t1.Name
FROM tablename t1 INNER JOIN tablename t2
ON t2.Name = t1.Name AND ABS(DATEDIFF(t1.Date, t2.Date)) = 1
GROUP BY t1.Name, t1.Date
HAVING COUNT(DISTINCT t2.Date) = 2;
Or, a more scalable solution:
SELECT DISTINCT t1.Name
FROM tablename t1 INNER JOIN tablename t2
ON t2.Name = t1.Name AND t2.Date BETWEEN t1.Date AND t1.Date + INTERVAL 2 DAY
GROUP BY t1.Name, t1.Date
HAVING COUNT(DISTINCT t2.Date) = 3;
You can remove DISTINCT from COUNT(DISTINCT t2.Date) if the combination of Name and Date is unique in the table.
See the demo.

Related

SQL select rows with number in sequence

I have 1:N table where every entity may have asigned multiple numbers.
ID Number
1 10
1 13
1 11
1 12
1 16
2 11
2 12
2 13
2 10
Now,I want all IDs which have for example 3 numbers in ascending sequence. I do not specify which numbers I want, I just want the SQL to return me all possible combinations it can find but the numbers has to be in ascending sequence and the sequence must contain exactly 3 numbers. The numbers are allways integers of any value. The numbers in result have to be next to each other (12,13,16)is not valid result.
For 3 numbers in this example it would be :
ID 1 : (10,11,12),(11,12,13)
ID 2 : (11,12,13),(10,11,13)
For 2 numbers in this example it would be:
ID 1 : (10,11),(11,12),(12,13)
ID 2 : (11,12)(12,13)
Is this possible in SQL select? Thanx
A solution whats comes close to your expected output.
Involves using self inner joins incombination with CONCAT_WS, GROUP_CONCAT..
For group of three you use this query
Query
SET SESSION group_concat_max_len = ##max_allowed_packet
SELECT
records.ID
, GROUP_CONCAT(CONCAT('(', records.number, ')'))
FROM (
SELECT
DISTINCT
table11.ID
, CONCAT_WS(
','
, table11.Number
, table12.Number
, table13.Number
) AS number
FROM
Table1 AS table11
INNER JOIN
Table1 AS table12
ON
table11.Number + 1 = table12.Number
INNER JOIN
Table1 table13
ON
table12.Number + 1 = table13.Number
ORDER BY
table11.ID ASC
, table11.Number ASC
) AS records
GROUP BY
records.ID
Result
| ID | GROUP_CONCAT(CONCAT('(', records.number, ')')) |
|----|------------------------------------------------|
| 1 | (11,12,13),(10,11,12) |
| 2 | (11,12,13),(10,11,12) |
see demo http://sqlfiddle.com/#!9/c5dfce/39
Simply use join. This produces a result set with each examples of sequential numbers on a different row:
select id, t1.number, t2.number, t3.number
from t t1 join
t t2
on t2.id = t1.id and t2.number = t1.number + 1 join
t t3
on t3.id = t2.id and t3.number = t2.number + 1;
If you really wanted a list, you would simply do:
select id,
group_concat('(', t1.number, ',', t2.number, ',', t3.number, ')') as groups
from t t1 join
t t2
on t2.id = t1.id and t2.number = t1.number + 1 join
t t3
on t3.id = t2.id and t3.number = t2.number + 1
group by t1.id;

Get distinct values in union all in hive

I have a table in hive that looks something like this
cust_id prod_id timestamp
1 11 2011-01-01 03:30:23
2 22 2011-01-01 03:34:53
1 22 2011-01-01 04:21:03
2 33 2011-01-01 04:44:09
3 33 2011-01-01 04:54:49
so on and so forth.
For each record I want to check that how many unique products has this customer bought within the last 24 hrs excluding the current transaction. So the output should look something like this -
1 0
2 0
1 1
2 1
3 0
My hive query looks something like this
select * from(
select t1.cust_id, count(distinct t1.prod_id) as freq from temp_table t1
left outer join temp_table t2 on (t1.cust_id=t2.cust_id)
where t1.timestamp>=t2.timestamp
and unix_timestamp(t1.timestamp)-unix_timestamp(t2.timestamp) < 24*60*60
group by t1.cust_id
union all
select t.cust_id, 0 as freq from temp_table t2
)unioned;
Just get all the rows for last 24 hours do a group by on custid and count(distinct productid) -1 as the output. Overall query would look something like this.
select cust_id, COUNT(distinct prod_id) - 1 from table_name where
unix_timestamp(t1.timestamp)-unix_timestamp(t2.timestamp) < 24*60*60
GROUP BY cust_id
*I am subtracting 1 here to exclude the latest transactionid of the user. (hope this is what you meant)
You can join to a derived table that contains the distinct # of products purchased in the past 24 hours for each customer/timestamp pair.
select t1.cust_id, t1.prod_id, t1.timestamp, t2.count_distinct_prod_id - 1
from mytable t1
join (
select t2.cust_id, t2.timestamp, count(distinct t3.prod_id) count_distinct_prod_id
from mytable t2
join mytable t3 on t3.cust_id = t2.cust_id
where unix_timestamp(t2.timestamp) - unix_timestamp(t3.timestamp) < 24*60*60
group by t2.cust_id, t2.timestamp
) t2 on t1.cust_id = t2.cust_id and t1.timestamp = t2.timestamp

Select rows from a table based on max value in different table

I'm looking for a MySQL query to extract values like in the following example:
TABLE1:
ID name
25 ab
24 abc
23 abcd
22 abcde
21 abcdef
TABLE2:
ID ID_TABLE1 total
1 25 0
2 25 1
3 25 2
4 25 3
5 23 1
6 22 0
7 22 1
8 21 0
9 21 2
10 24 10
11 24 7
I want to return all TABLE1 rows where max value of total column (in TABLE2) is < 3.
So the results should be:
ID name
23 abcd
22 abcde
21 abcdef
I tried this:
SELECT t1.*
FROM TABLE1 t1
INNER JOIN (
SELECT MAX( total ) AS max_total, ID_TABLE1
FROM TABLE2
GROUP BY total, ID_TABLE1
) t2
ON t1.ID = t2.ID_TABLE1
WHERE t2.max_total < 3
but it's not the result I want.
Try this:
SELECT t1.ID, t1.name
FROM TABLE1 t1
INNER JOIN (SELECT ID_TABLE1, MAX(total) AS max_total
FROM TABLE2
GROUP BY ID_TABLE1
) t2 ON t1.ID = t2.ID_TABLE1
WHERE t2.max_total < 3;
Your inner query groups the results by id_table and by total. Since the maximum of total per total is the value itself, it makes the inner query somewhat meaningless. Just remove the total from the group by clause and you should be OK:
SELECT t1.*
FROM TABLE1 t1
INNER JOIN (
SELECT MAX( total ) AS max_total, ID_TABLE1
FROM TABLE2
GROUP BY ID_TABLE1
) t2
ON t1.ID = t2.ID_TABLE1
WHERE t2.max_total < 3
SELECT t1.*
FROM TABLE1 t1
INNER JOIN (
SELECT MAX( total ) AS max_total, ID_TABLE1
FROM TABLE2
GROUP BY ID_TABLE1
having t2.max_total < 3
) t2
ON t1.ID = t2.ID_TABLE1
Here is a way to do using left join without using any subquery and group by clauses.
select t1.* from table1 t1
join table2 t2
on t1.id = t2.id_table1
left join table2 t3 on
t2.id_table1 = t3.id_table1
and t2.total < t3.total
where t3.id is null
and t2.total < 3
Another way is
select t1.* from table1 t1
join table2 t2 on t1.id = t2.id_table1
where not exists(
select 1 from table2 t3
where t2.id_table1 = t3.id_table1
and t2.total < t3.total
)
and t2.total < 3;
SELECT t1.*
FROM TABLE1 t1
INNER JOIN (
SELECT MAX( total ) AS max_total, ID_TABLE1
FROM TABLE2
where total > 3 GROUP BY total, ID_TABLE1
) t2
ON t1.ID != t2.ID_TABLE1
There's a simpler way without using GROUP or MAX:
SELECT * FROM table1
WHERE id NOT IN (
SELECT id_table1 FROM table2 WHERE total >= 3
);
The subquery selects all rows in table2 that have a total >= 3. Then we select those rows from table1 that are not in the subquery result.

How to not show brands that was serviced the last month

I have this code where it shows the brands that was serviced last month
SELECT t1.Brand, t1.ID,
DATE_FORMAT((t2.Tstamp), "%m/%e/%Y") AS "Last Serviced",
FROM Table1 t1
INNER JOIN Table2 t2
ON t1.ID = t2.Equip_ID
WHERE
MONTH(t2.Tstamp) <> "01"
Group by t1.ID
Order by t2.Tstamp
Output of that is
Brand | ID | Last Serviced
Polo | 1 | 12/12/2013
Shirt | 2 | 12/24/2013
Then if i do another service on the brand Polo, it will insert a new row for the month of January. What I want to happen is since i did service on Polo then it won't appear on the list anymore, the list should be
Brand | ID | Last Serviced
Shirt | 2 | 12/24/2013
How do I that? What else do I add?
You want brands where the most recent service date is last month. For that, use having:
SELECT t1.Brand, t1.ID, DATE_FORMAT(t2.Tstamp, "%m/%e/%Y") AS "Last Serviced",
FROM Table1 t1 INNER JOIN
Table2 t2
ON t1.ID = t2.Equip_ID
Group by t1.ID
having MONTH(max(t2.Tstamp)) <> "01"
Order by t2.Tstamp;
I would include the year and month in the comparison, but this follows the logic of your original query.
EDIT:
To include year in the query, base the comparisons on the current date:
SELECT t1.Brand, t1.ID, DATE_FORMAT(t2.Tstamp, "%m/%e/%Y") AS "Last Serviced",
FROM Table1 t1 INNER JOIN
Table2 t2
ON t1.ID = t2.Equip_ID
Group by t1.ID
having year(max(t2.Tstamp)) <> year(now()) and
month(max(t2.Tstamp)) <> month(now())
Order by t2.Tstamp;
Using CASE statements. Let`s see:
SELECT t1.Brand, t1.ID,
case(when MONTH(t2.Tstamp)=01 then NULL else DATE_FORMAT(t2.Tstamp, "%m/%e/%Y") end ) as `Last Serviced`
from FROM Table1 t1 INNER JOIN Table2 t2 ON t1.ID = t2.Equip_ID
Group by 2 Order by 3

Mysql derived table

Suppose i have a mysql table name table with fields
rank
date
id
The values are like:
10, 2012-01-01, 3
9, 2012-01-04, 3
5, 2012-01-07, 3
3, 2012-01-10, 3
10, 2012-01-01, 4
6, 2012-01-04, 4
7, 2012-01-07, 4
In a single sql, how can i get both last and first values sorted by date grouped by id?
I know how to get first one or last one
SELECT rank, id FROM
(SELECT rank, id FROM table ORDER BY date DESC) s GROUP BY id;
I would like that the fields returned to be somethink like: lastrank, firstrank and id.
Thank you
Try this:
select id,
max(if(MyOrder = 1, rank, null)) as FirstRank,
max(if(MyOrder = 2, rank, null)) as LastRank
from (
select t1.id, t1.rank, 1 MyOrder from t t1
left join t t2 on
t1.id = t2.id and t1.date > t2.date
where t2.date is null
union
select t1.id, t1.rank, 2 from t t1
left join t t2 on
t1.id = t2.id and t1.date < t2.date
where t2.date is null
) s
group by id
The result of this query taking your sampla data as input is:
+----+-----------+----------+
| ID | FIRSTRANK | LASTRANK |
+----+-----------+----------+
| 3 | 10 | 3 |
| 4 | 10 | 7 |
+----+-----------+----------+
I'm not fully sure that I understand your question, but I'm going to try to answer anyway.
SELECT min(rank), max(rank), id
FROM table
ORDER BY date DESC
GROUP BY id;
When grouping, you can use aggregate functions on the results to get specific samples from the groups.
Try this query -
SELECT
t1.*,
IF(t1.date = t2.min_date, 'FIRSTRANK', 'LASTRANK') rank_type
FROM table_rank t1
JOIN (
SELECT id, MAX(date) max_date, MIN(date) min_date FROM table_rank GROUP BY id
) t2
ON t1.id = t2.id AND (t1.date = t2.min_date OR t1.date = t2.max_date)
Involve GROUP_CONCAT(rank ORDER BY date), and use SUBSTRING_INDEX.