I have a table with photos
id | year| comm_count
0 2015 1
1 2016 2
2 2017 5
3 2018 7
4 2019 1
5 2020 9
6 2021 1
7 2022 1
I select photo with a given id, somewhere in the middle of all photos. For example like this:
SELECT *
FROM photo
WHERE year > '2017'
ORDER BY comm_count DESC, year DESC
This will give me:
5,3,7,6,4
This gives me list of all photos. Now, I write this list on my web, but user can click on one certain photo. After that, detailed page opens. But from this detailed page, I would like to be able to go to "next" M and "previous" N photos. This means, I need to select neihboring IDs based on currenttly selected one. How can this be done?
Now I select id = 7 and I want neighbors to be:
prev: 5,3 and next: 6,4. How can this be selected?
SqlFiddle - http://sqlfiddle.com/#!9/4f3f42/4/0
I cannot run the same query and filter results in PHP, because query can contain LIMITS (eg. with LIMIT 2, 4 I still the need correct neighbors)
Once you have the year and comm_count values for the selected row with id=7, you can make two simple queries:
SELECT * FROM photo
WHERE year > 2017 AND (comm_count = 1 AND year <= 2022 OR comm_count < 1)
ORDER BY comm_count DESC, year DESC LIMIT 3 OFFSET 1
+----+------+------------+
| id | year | comm_count |
+----+------+------------+
| 6 | 2021 | 1 |
| 4 | 2019 | 1 |
+----+------+------------+
SELECT * FROM photo
WHERE year > 2017 AND (comm_count = 1 AND year >= 2022 OR comm_count > 1)
ORDER BY comm_count ASC, year ASC LIMIT 3 OFFSET 1;
+----+------+------------+
| id | year | comm_count |
+----+------+------------+
| 3 | 2018 | 7 |
| 5 | 2020 | 9 |
+----+------+------------+
If you use MySQL 8.0, you can use the LAG() and LEAD() functions.
SELECT id, year,
LAG(id, 1) OVER w AS next,
LAG(id, 2) OVER w AS next_next,
LEAD(id, 1) OVER w AS prev,
LEAD(id, 2) OVER w AS prev_prev
FROM photo
WHERE year > 2017
WINDOW w AS (ORDER BY comm_count DESC, year DESC)
+----+------+------+-----------+------+-----------+
| id | year | next | next_next | prev | prev_prev |
+----+------+------+-----------+------+-----------+
| 5 | 2020 | NULL | NULL | 3 | 7 |
| 3 | 2018 | 5 | NULL | 7 | 6 |
| 7 | 2022 | 3 | 5 | 6 | 4 |
| 6 | 2021 | 7 | 3 | 4 | NULL |
| 4 | 2019 | 6 | 7 | NULL | NULL |
+----+------+------+-----------+------+-----------+
Related
Here is a part of table from which I am retrieving data for the last 3 months including current month
+-------------+-----------------------+
| Wo_id | updated_at |
+-------------+-----------------------+
| 1 | 2018-12-05 10:38:06 |
| 2 | 2018-12-02 15:21:17 |
| 3 | 2018-12-01 22:18:53 |
| 4 | 2018-10-25 10:38:06 |
| 5 | 2018-10-18 15:21:17 |
| 6 | 2018-10-16 22:18:53 |
| 7 | 2018-10-19 10:26:19 |
| 8 | 2018-10-27 07:06:52 |
| 9 | 2018-09-25 11:35:09 |
| 10 | 2018-09-18 12:54:27 |
The query I tried is
SELECT MONTHNAME(updated_at) month,YEAR(updated_at) year_name,
MONTH(updated_at) month_no, COUNT(*) work_orders
FROM work_orders where updated_at >= last_day(now()) + interval 1 day - interval 3 month
GROUP by MONTH(updated_at),YEAR(updated_at)
ORDER BY MONTH(updated_at) DESC
The Output I am getting is
+-------------+-------------+----------+-------------+
| month | year_name | month_no | work_orders |
+-------------+-------------+----------+-------------+
| December | 2018 | 12 | 3 |
| October | 2018 | 10 | 5 |
| September | 2018 | 9 | 2 |
As you can see the query is neglecting November as its data is not in the table. It is Including September in order to complete the cycle of 3 months which is wrong. I want the output like this
+-------------+-------------+----------+-------------+
| month | year_name | month_no | work_orders |
+-------------+-------------+----------+-------------+
| December | 2018 | 12 | 3 |
| November | 2018 | 9 | 0 |
| October | 2018 | 10 | 5 |
Can someone guide me in modifying the above mentioned query. Thanks
You need to create a table of the last three months and then LEFT JOIN that to your work orders table (using the month of the work order) to get the results you want. The table of the last 3 months can be generated using a UNION:
SELECT NOW() AS month
UNION
SELECT NOW() - INTERVAL 1 MONTH
UNION
SELECT NOW() - INTERVAL 2 MONTH
Output (as of 2018-12-07):
month
2018-12-07 11:06:15
2018-11-07 11:06:15
2018-10-07 11:06:15
Note that it is OK to subtract 1 month from the date as if the day number is larger than the number of days in the previous month it will be adjusted downward to make the date valid (see the manual).
The final query then becomes:
SELECT MONTHNAME(m.month) AS month_name, YEAR(m.month) AS year_name,
MONTH(m.month) AS month_no, COUNT(wo.Wo_id) work_orders
FROM (SELECT NOW() AS month
UNION
SELECT NOW() - INTERVAL 1 MONTH
UNION
SELECT NOW() - INTERVAL 2 MONTH) m
LEFT JOIN work_orders wo ON MONTH(wo.updated_at) = MONTH(m.month) AND
YEAR(wo.updated_at) = YEAR(m.month)
GROUP by m.month, year_name
ORDER BY m.month DESC
Note that we don't need a WHERE clause as the values in the month table restrict the data to the last 3 months that we are interested in. Also we use a LEFT JOIN so that we get a result for each month even if there were no work orders that month.
Output:
month_name year_name month_no work_orders
December 2018 12 3
November 2018 11 0
October 2018 10 5
Demo on dbfiddle
I have a MYSQL table where I need to get to subtract values from 2 different rows.
This is my DB table:
Tablename: ext_partnertotals
| Partner_ID | Partnername | Month | Year | Total |
|------------|-------------|-------|------|-------|
| 1 | Partner 1 | 1 | 2018 | 10 |
| 1 | Partner 1 | 2 | 2018 | 12 |
| 2 | Partner 2 | 1 | 2018 | 18 |
| 2 | Partner 2 | 2 | 2018 | 12 |
It should get this with a query:
| Partner_ID | Partnername | up/down |
|------------|-------------|---------|
| 1 | Partner 1 | +2 |
| 2 | Partner 2 | -6 |
I need to get the Subtract value of 2 different months for each Partner.
Every Partner has a tablerow for each month and a value for that month.
Now I need to get If they went up or went down in value since the month before.
Can someone write me a query?
Since you're unable to improve your terrible schema, I recommend you use a (very ugly/hard to maintain and very slow) correlated subquery:
SELECT Partner_ID, Partnername, Year, Month, Total - (
SELECT Total
FROM ext_partnertotals AS prev
WHERE prev.Partner_ID = cur.Partner_ID AND CASE cur.Month
WHEN 1 THEN prev.Year = cur.Year - 1 AND prev.Month = 12
ELSE prev.Year = cur.Year AND prev.Month = cur.Month - 1
END
) AS `up/down` FROM ext_partnertotals AS cur
See it on sqlfiddle.
I have a table:
UNIT_ID | YEAR | MONTH | VAR
---------+------+-------+------
1 | 2015 | 1 | 0
1 | 2015 | 2 | 0
1 | 2015 | 3 | 0
2 | 2015 | 1 | 10
2 | 2015 | 2 | 10
2 | 2015 | 3 | 10
1 | 2015 | 4 | 5
1 | 2015 | 5 | 5
1 | 2015 | 6 | 5
2 | 2015 | 4 | 10
2 | 2015 | 5 | 3
2 | 2015 | 6 | 3
3 | 2016 | 1 | 3
3 | 2016 | 2 | 3
3 | 2016 | 3 | 3
3 | 2016 | 4 | 3
2 | 2016 | 6 | 0
2 | 2016 | 7 | 0
2 | 2016 | 8 | 0
I want to know which units have a sequence bigger than 3 zeros or bigger than 4 values repeated. Grouped by year. So, my result table would be like this:
1 | 2015 | true
2 | 2015 | true
2 | 2016 | true
I have found this solution but unfortunately I could not adapt to my case. I need also that the query is in MySQL.
You could just join them 4 times. Last join is a left join to allow the case for 3 0's.
select a.unit_id, a.year, 'true'
from tbl a
join tbl b on a.unit_id = b.unit_id and a.year = b.year and a.month+1 = b.month and a.var = b.var
join tbl c on b.unit_id = c.unit_id and b.year = c.year and b.month+1 = c.month and b.var = c.var
left join tbl d on c.unit_id = d.unit_id and c.year = d.year and c.month+1 = d.month and c.var = d.var
where a.var = 0 or d.var is not null;
Faster and more generic solution. It scans the table only once, and uses user defined variables (#pu for previous unit_id, #py for previous year, etc) to remember the previous row:
select distinct unit_id, year
from (
select unit_id, `year`, `month`, `var`,
if(unit_id=#pu and `year`=#py and `month`=#pm+1 and `var`=#pv, #i:=#i+1, #i:=1)*
if(#pu:=unit_id,1,1)*if(#py:=`year`,1,1)*if(#pm:=`month`,1,1)*if(#pv:=`var`,1,1) as c
from table1 a
join (select #pu:=null, #py:=null, #pm:=null, #pv:=null, #i:=1) b
order by unit_id, `year`, `month`, `var`) a
group by unit_id, `year`, `var`
having (`var` = 0 and max(c) >= 3) or (`var` != 0 and max(c) >= 4);
fiddle
I need to populate my fact table with data from lds_placement table. I have selected the records and here is what it looks like:
fk1_account_id | fk3_job_role_id | salary | no_of_placements | YEAR
---------------------------------------------------------------------
10 | 3 | 165000 | 5 | 2010
10 | 3 | 132000 | 4 | 2011
10 | 3 | 132000 | 4 | 2012
20 | 2 | 990000 | 3 | 2010
20 | 2 | 132000 | 2 | 2011
20 | 2 | 132000 | 2 | 2012
I want to insert time_id from a different table called time_dim into the column year and not the actual year itself.
The time_dim table looks like this:
time_id | year
---------------
5 | 2015
1 | 2013
2 | 2010
3 | 2014
4 | 2012
6 | 2011
I need to insert into "year" column is actually:
year
2
6
4
2
6
4
Please give me the way to insert time_id instead of year in the table.
Here is the code I used to select the top-most table.
SELECT
fk1_account_id,
fk3_job_role_id,
Sum(actual_salary) AS salary,
Count(1) AS no_of_placements,
MAX(EXTRACT(YEAR FROM plt_estimated_end_date)) AS year
FROM lds_placement
GROUP BY fk1_account_id, fk3_job_role_id, EXTRACT(YEAR FROM plt_estimated_end_date)
ORDER BY fk1_account_id;
Use a left join if you want to capture records where year doesn't exist in time_dim. Else use inner_join.
select t.fk1_account_id,t.fk3_job_role_id,t.salary,t.no_of_placements
,d.time_id
from
(SELECT fk1_account_id, fk3_job_role_id, Sum(actual_salary) as salary, Count(1) as no_of_placements, MAX(EXTRACT(YEAR FROM plt_estimated_end_date)) AS YEAR
FROM lds_placement
GROUP BY fk1_account_id, fk3_job_role_id, EXTRACT(YEAR FROM plt_estimated_end_date)
)t
left join time_dim d
on t.year=d.year
order by t.fk1_account_id
I have a table where each data has a date when it was inserted in form of two columns, year and week:
+------+------+-------+
| Week | Year | Value |
+------+------+-------+
| 1 | 2014 | 5 |
| 5 | 2014 | 23 |
| 6 | 2014 | 12 |
| 7 | 2014 | 43 |
| 8 | 2014 | 4 |
| 9 | 2014 | 2 |
| 26 | 2013 | 21 |
| 27 | 2013 | 17 |
| 28 | 2013 | 42 |
| 31 | 2013 | 5 |
| ... | ... | .. |
+------+------+-------+
I need a query to get data that was inserted between two dates (year, week). I guess that it should be alternative to intersection of two queries, one with a starting date and the second with ending data, but I can't get it to work. Any help or sugestion?
Here is my shot but INTERSECT is not supported in MySql:
(SELECT SUM(Duration), Week, Type, Year
FROM UP26rotordowntime
WHERE (Year>=2013)
AND (Week >=01)
GROUP BY Week)
INTERSECT
(SELECT SUM(Duration), Week, Type, Year
FROM UP26rotordowntime
WHERE (Year<=2014)
AND (Week <=14)
GROUP BY Week)
You can put simple logic in WHERE conditions and use (year,week) pairs for GROUP BY:
SELECT SUM(Duration), Week, Type, Year
FROM UP26rotordowntime
WHERE Year = 2005 AND Week >= 10
OR Year BETWEEN 2006 AND 2013
OR Year = 2014 AND Week <= 14
GROUP BY Year,Week
If you have id the query is very simple:
SELECT SUM(Duration), Week, Type, Year FROM UP26rotordowntime
WHERE ID IN (
SELECT ID FROM UP26rotordowntime WHERE (Year>=2013) AND (Week >=01) )
OR ID IN (
SELECT ID FROM UP26rotordowntime WHERE (Year<=2014) AND (Week <=14))
GROUP BY Week
This should return you the intersect you need