I have a MYSQL table with two ID columns and one date column. I am trying to get the number of consecutive weeks based on the date column
|ID|ID2|Date |
|1 | 1 |2018-01-01|
|1 | 1 |2018-01-08|
|1 | 1 |2018-01-15|
|2 | 1 |2018-01-01|
|2 | 1 |2018-01-08|
|2 | 2 |2018-01-01|
What i am trying to achieve is a table like this
|ID |ID2|Consecutive Week|
|1 | 1 |3 |
|2 | 1 |2 |
|2 | 2 |1 |
I'm stuck with the following code:
SELECT a.ID, a.ID2 ,consec_set, COUNT(1) AS consec_count
FROM
(
SELECT IF(b.Date IS NULL, #val:=#val+1, #val) AS consec_set,a.ID2
FROM tbl a
CROSS JOIN (SELECT #val:=0) var_init
LEFT JOIN tbl b ON
a.ID = b.ID AND
a.ID2 = b.ID2 AND
YEARWEEK(a.Date ,1) = YEARWEEK(b.Date ,1) + 1
WHERE a.ID= 1
) a
GROUP BY a.consec_set;
I reached this code following several guides here but they were only with consecutive days and one ID only. Thanks in advance.
Somehow I succeeded the SQL. A bit complicated though.
SELECT id, id2, Max(cnt) FROM (
SELECT id, id2, Count(id) AS CNT FROM (
SELECT *, (
SELECT #num:=Ifnull(#num,0)) AS grouper,
#num := IF(cntn=0, #num+1, #num) AS row_number
FROM (
SELECT a.id, a.id2, a.wno, b.id IS NOT NULL AS CNTN
FROM (
SELECT DISTINCT id, id2, Week(pdate) AS WNO FROM listing) a
LEFT JOIN
(SELECT DISTINCT id, id2, Week(pdate) AS WNO FROM listing) b
ON a.wno = b.wno - 1 AND a.id = b.id AND a.id2 = b.id2
ORDER BY a.id, a.id2, a.wno
) d
) e GROUP BY e.id, e.id2, e.grouper
) f GROUP BY f.id, f.id2
Heres the fiddle with all the cases I said in the comments included:
http://sqlfiddle.com/#!9/b785df/71
Explaining from the inside out:
Selected the distinct WeekNumber, ID, ID2 values from the table
Joined the 1. table to itself again on the consecutive ones if it exists
Converted the joined rows into booleans whether the consecutive record exists or not.
Grouped the consecutive rows with indexes like 1,2,3 etc
Selected the count of the groups
Selected the maximum of the groups of which have the same ID and ID1's.
Not an answer. Too long for a comment.
Consider the following:
SELECT *, YEARWEEK(date,1) yw FROM my_table;
+-----+-----+------------+--------+
| ID1 | ID2 | Date | yw |
+-----+-----+------------+--------+
| 1 | 1 | 2018-01-01 | 201801 |
| 1 | 1 | 2018-01-08 | 201802 |
| 1 | 1 | 2018-01-15 | 201803 |
| 2 | 1 | 2018-01-01 | 201801 |
| 2 | 1 | 2018-01-07 | 201801 |
| 2 | 2 | 2018-01-01 | 201801 |
+-----+-----+------------+--------+
1,1 = 3 consecutive weeks
2,1 = 1 week
2,2 = 1 week
Related
I am trying to form a mysql query where I want to concat all the previous values until the current row -1 . For example
select * from a;
+------+
| id |
+------+
| 1 |
| 3 |
| 4 |
+------+
Desired o/p
+------+============
| id |concat_prev_to_cur
+------+============
| 1 |null
| 3 |1
| 4 |1,3
+------+============
Can this be achieved with using SELECT only
Tried this but this doesn't work
with recursive b as (select id from a union all select concat(a.id,',',b.id) from b join a on a.id=b.id) select * from b;
Update: This seems to be close to the desired output
With b as (Select id, row_number() Over(order by id) r from a) select c.id,group_concat(b.id) from b join b c on b.r < c.r group by c.r ;
+------+--------------------+
| id | group_concat(b.id) |
+------+--------------------+
| 3 | 1 |
| 4 | 1,3 |
+------+--------------------+
Maybe a recursive query is not needed.
SELECT id, (
SELECT GROUP_CONCAT(id) AS ids
FROM a AS agg
WHERE agg.id < a.id
) AS ids
FROM a
db<>fiddle
I want to count the product_name in these tables together:
jan_product feb_product
+------------+ +------------+
|product_name| |product_name|
+------------+ +------------+
|A | |A |
+------------+ +------------+
|A | |B |
+------------+ +------------+
|C | |C |
+------------+ +------------+
I want my result to look like:
+------------+---------+---------+
|product_name|jan_count|feb_count|
+------------+---------+---------+
|A |2 |1 |
+------------+---------+---------+
|B |0 |1 |
+------------+---------+---------+
|C |1 |1 |
+------------+---------+---------+
So I tried the query below (I'm using MYSQL so I couldnt try FULL JOIN):
SELECT
j.product_name,
count(j.product_name) as jan_count,
count(f.product_name) as feb_count
FROM jan_product as j
JOIN feb_product as f
ON j.product_name = f.product_name
group by j.product_name
UNION
SELECT
f.product_name,
count(j.product_name) as jan_count,
count(f.product_name) as feb_count
FROM jan_product as j
RIGHT OUTER JOIN feb_product as f
ON f.product_name = j.product_name
group by f.product_name
;
But i got this instead:
+------------+---------+---------+
|product_name|jan_count|feb_count|
+------------+---------+---------+
|A |2 |2 | --- A counts for FEB is wrong
+------------+---------+---------+
|B |0 |1 |
+------------+---------+---------+
|C |1 |1 |
+------------+---------+---------+
I do not know what to do to get to the expected result.
We can do this with count from Union all. I would rather advise you to have one table with a month column.
create table jan (pname char(1));
create table feb (pname char(1));
insert into jan values('A'),('A'),('C');
insert into feb values ('A'),('B'),('C');
select
pname,
count(j) jan,
count(f) feb
from
(select pname,pname j,null f from jan
union all
select pname,null,pname from feb) jf
group by pname
pname | jan | feb
:---- | --: | --:
A | 2 | 1
B | 0 | 1
C | 1 | 1
db<>fiddle here
Ignoring the table structure for a minute ... try using a UNION ALL, including an extra column to indicate the source month. Then use a conditional SUM to calculate the totals for each month.
See also db<>fiddle
SELECT t.product_name
, SUM( CASE WHEN t.month_number = 1 THEN 1 ELSE 0 END) AS jan_count
, SUM( CASE WHEN t.month_number = 2 THEN 1 ELSE 0 END) AS feb_count
FROM (
SELECT CAST(1 AS UNSIGNED) AS month_number, product_name
FROM jan_product
UNION ALL
SELECT CAST(2 AS UNSIGNED) AS month_number, product_name
FROM feb_product
) t
GROUP BY t.product_name
Results:
product_name | jan_count | feb_count
:----------- | --------: | --------:
A | 2 | 1
C | 1 | 1
B | 0 | 1
Having said that, you should normalize the model. You could greatly simplify things by storing everything in a single table, with a date (or month + year columns) instead of having a separate table for each month.
Also, it seems like you're storing information about events that occur to a specific product over time. If that's the case, you should have a separate table containing unique products:
ProductId
ProductName
1
Product A
2
Product B
3
Product C
Other tables that store information about products should store the "Product" table's unique PK (primary key) value - not a product's name. For example, if you had a ProductSales table
| ProductId | SaleDate | Quantity |
|-----------|-------------|----------|
| 1 | 02/01/2022 | 15 |
| 2 | 02/10/2022 | 4 |
| 1 | 02/12/2022 | 3 |
| 3 | 02/01/2022 | 20 |
To retrieve information about the sales by month, all you'd need is a simple JOIN between the two tables
See also db<>fiddle
SELECT p.product_name
, year(s.sales_date) AS sales_year
, SUM( CASE month(s.sales_date) WHEN 1 THEN 1 ELSE 0 END) AS jan_sales
, SUM( CASE month(s.sales_date) WHEN 2 THEN 1 ELSE 0 END) AS feb_sales
, SUM( CASE month(s.sales_date) WHEN 3 THEN 1 ELSE 0 END) AS mar_sales
-- ... etc
FROM product p LEFT JOIN product_sales s ON s.product_id = p.product_id
GROUP BY p.product_name
, year(s.sales_date)
;
Results:
product_name | sales_year | jan_sales | feb_sales | mar_sales
:----------- | ---------: | --------: | --------: | --------:
Product A | 2022 | 0 | 2 | 0
Product B | 2022 | 0 | 1 | 0
Product C | 2022 | 0 | 1 | 0
--Please try using below query
-------
WITH cte AS
( SELECT * FROM jan_product
UNION
SELECT * FROM feb_product)
SELECT cte.product_name,
j.Jan_count,
count(f.product_name) as February_count
FROM cte
LEFT JOIN (SELECT product_name,
COUNT(product_name) as Jan_count
FROM jan_product
GROUP BY product_name) j
ON cte.product_name=j.product_name
LEFT JOIN (SELECT product_name,
COUNT(product_name) as Feb_count
FROM feb_product
GROUP BY product_name) f
ON cte.product_name=f.product_name
Lets say I have a table with few fields, e.g. A, B, C, D
I need to group by field A, and select most occuring values from B, C, D.
Example:
+---+---+---+----+
| A | B | C | D |
+---+---+---+----+
| 1 | 3 | 5 | 15 |
+---+---+---+----+
| 1 | 5 | 6 | 32 |
+---+---+---+----+
| 1 | 5 | 6 | 34 |
+---+---+---+----+
| 2 | 7 | 5 | 50 |
+---+---+---+----+
| 2 | 8 | 1 | 32 |
+---+---+---+----+
Expected result:
+---+---+---+----+
| A | B | C | D |
+---+---+---+----+
| 1 | 5 | 6 | 15 |
+---+---+---+----+
| 2 | 7 | 5 | 50 |
+---+---+---+----+
I saw a lot of examples how to select most occurring value from one column by using COUNT(*) and than using MAX on top of that. But what to do in this case ?
The query looks a bit complicated because you have to do it for 3 columns. The idea is to rank the rows by counts grouping by combinations of a-b,a-c,a-d and getting the first row for each combination. This is done using variables. In case of ties for counts the lowest value of b,c or d is returned. (this can be changed if the ordering needs to be reversed.) Finally one more aggregation is required to get corresponding values on to one row.
SQL Fiddle Demo
select a,max(b),max(c),max(d)
from (
select a,b,c,d
from (select a,b,c,d,
#rn:=case when #prev=a then #rn+1 else 1 end as rank,
#prev:=a
from (select a,b,null as c,null as d,count(*) as cnt
from tbl
group by a,b
) t
cross join (select #rn:=0,#prev:='') r
order by a,cnt desc,b
) t
where rank = 1
union all
select a,b,c,d
from (select a,b,c,d,
#rn:=case when #prev=a then #rn+1 else 1 end as rank,
#prev:=a
from (select a,null as b,c,null as d,count(*) as cnt
from tbl
group by a,c
) t
cross join (select #rn:=0,#prev:='') r
order by a,cnt desc,c
) t
where rank = 1
union all
select a,b,c,d
from (select a,b,c,d,
#rn:=case when #prev=a then #rn+1 else 1 end as rank,
#prev:=a
from (select a,null as b,null as c,d,count(*) as cnt
from tbl
group by a,d
) t
cross join (select #rn:=0,#prev:='') r
order by a,cnt desc,d
) t
where rank = 1
) t
group by a
For every ID_Number, there is a bill_date and then two types of bills that happen. I want to return the latest date (max date) for each ID number and then add together the two types of bill amounts. So, based on the table below, it should return:
| 1 | 201604 | 10.00 | |
| 2 | 201701 | 28.00 | |
tbl_charges
+-----------+-----------+-----------+--------+
| ID_Number | Bill_Date | Bill_Type | Amount |
+-----------+-----------+-----------+--------+
| 1 | 201601 | A | 5.00 |
| 1 | 201601 | B | 7.00 |
| 1 | 201604 | A | 4.00 |
| 1 | 201604 | B | 6.00 |
| 2 | 201701 | A | 15.00 |
| 2 | 201701 | B | 13.00 |
+-----------+-----------+-----------+--------+
Then, if possible, I want to be able to do this in a join in another query, using ID_Number as the column for the join. Would that change the query here?
Note: I am initially only wanting to run the query for about 200 distinct ID_Numbers out of about 10 million. I will be adding an 'IN' clause for those IDs. When I do the join for the final product, I will need to know how to get those latest dates out of all the other join possibilities. (ie, how do I get ID_Number 1 to join with 201604 and not 201601?)
I would use NOT EXISTS and GROUP BY
select, t1.id_number, max(t1.bill_date), sum(t1.amount)
from tbl_charges t1
where not exists (
select 1
from tbl_charges t2
where t1.id_number = t2.id_number and
t1.bill_date < t2.bill_date
)
group by t1.id_number
the NOT EXISTS filter out the irrelevant rows and GROUP BY do the sum.
I would be inclined to filter in the where:
select id_number, sum(c.amount)
from tbl_charges c
where c.date = (select max(c2.date)
from tbl_charges c2
where c2.id_number = c.id_number and c2.bill_type = c.bill_type
)
group by id_number;
Or, another fun way is to use in with tuples:
select id_number, sum(c.amount)
from tbl_charges c
where (c.id_number, c.bill_type, c.date) in
(select c2.id_number, c2.bill_type, max(c2.date)
from tbl_charges c2
group by c2.id_number, c2.bill_type
)
group by id_number;
I have the following table:
+----+-----------+-----------+
| id | teacherId | studentId |
+----+-----------+-----------+
| 1 | 1 | 4 |
| 2 | 1 | 2 |
| 3 | 1 | 1 |
| 4 | 1 | 3 |
| 5 | 2 | 2 |
| 6 | 2 | 1 |
| 7 | 2 | 3 |
| 8 | 3 | 9 |
| 9 | 3 | 6 |
| 10 | 1 | 6 |
+----+-----------+-----------+
I need a query to find two teacherId's with maximum number of common studentId's.
In this case teachers with teacherIds 1,2 have common students with studentIds 2, 1, 3, which is greater than 1,3 having common students 6.
Thanks in Advance!
[Edit]: After several hours I've had the following solution:
SELECT * FROM (
SELECT r1tid, r2tid, COUNT(r2tid) AS cnt
FROM (
SELECT r1.teacherId AS r1tid, r2.teacherId AS r2tid
FROM table r1
INNER JOIN table r2 ON r1.studentId=r2.studentId AND r1.teacherId!=r2.teacherId
ORDER BY r1tid
) t
GROUP BY r1tid, r2tid
ORDER BY cnt DESC
) t GROUP BY cnt ORDER BY cnt DESC LIMIT 1;
I was sure that there must exist more short and elegant solution, but I could not find it.
You would do this with a self-join. Assuming no duplicates in the table:
select t.teacherid, t2.teacherid, count(*) as NumStudentsInCommon
from table t join
table t2
on t.studentid = t2.studentid and
t.teacherid < t2.teacherid
group by t.teacherid, t2.teacherid
order by NumStudentsInCommon desc
limit 1;
If you had duplicates, you would just replace count(*) with count(distinct studentid), but count(distinct) requires a bit more work.
select t.teacherId, t2.teacherId, sum(t.studentId) as NumStudentsInCommon
from table1 t join
table1 t2
on t.studentId = t2.studentId and
t.teacherId < t2.teacherId
group by t.teacherId, t2.teacherId
order by NumStudentsInCommon desc