I'm working with ACL in CakePHP, and would like to fetch all entries that are allowed for the current user.
Simplified, I have two tables:
Interval:
**lft**| **rght**
10 | 20
40 | 60
90 | 92
Acos:
**foreign_key** | **lft** | **rght**
3 | 15 | 17
4 | 25 | 27
5 | 45 | 47
6 | 49 | 51
7 | 81 | 83
Now I would like to fetch the foreign_keys FROM Acos which have lft and rght values between the lft and rght from Interval
In the above example we get foreign_key 3, 5, 6.
On a side note. The "Interval" table does not actually exist. The values came from this query (also the acos table):
SELECT lft, rght FROM acos WHERE id IN (
SELECT aco_id FROM aros_acos WHERE
aro_id = (SELECT parent_id FROM aros WHERE foreign_key = 48 && model = 'User' )
OR
aro_id = (SELECT id FROM aros WHERE foreign_key = 48 && model = 'User' )
)
I don't hope the example above is too messy. Please comment if there are any uncertainties.
Thank you in advance!
select distinct foreign_key
from Acos
join
(
SELECT lft, rght
FROM acos
WHERE
id IN (
SELECT aco_id
FROM
aros_acos a,
(
select parent_id, id
from aros
where foreign_key = 48 && model = 'User'
) x
WHERE
a.aro_id = x.id
OR a.aro_id = x.parent_id
)
) Interval
on
Acos.lft>=Interval.lft
and acos.rght<=Interval.rght
You can join the tables like this:
SELECT foreign_key from Acos a INNER JOIN Interval i ON
(i.lft <= a.lft AND i.rght>=a.rght)
The join condition (i.lft <= a.lft AND i.rght>=a.rght)ensures that range in Acos is within or equal to the range of Interval, without going over.
If there are multiple matching intervals, you'll get multiple rows in the result. Use a GROUP BY or DISTINCT to get just the foreign key.
Related
this board helped me a few times in the past.
My challange: I want to get the difference between the values within one column.
The table looks like this:
id | channel_id | timestamp | value
4515| 7 |1519771680000 | 7777
4518| 8 |1519772160000 | 6666
4520| 7 |1519772340000 | 8888
id: Internal ID from Datasource. In some cases it's ordered, in other cases not. We cannot thrust this order.
channel_id: Different data sources.
timestamp: unix timestamp.
value: measured value.
What I want to do:
Filter (e.g. channel_id = 7).
Calculate the difference between one timestamp and the next one. In this example: 8888-7777
I found an solution on another database but I cannot transfer it to mysql as the windows functions are very limited. Has somebody of you an idea how to get a solution which can be used in select statements?
Thx and KR
Holger
You can get the two rows to compare (ie subtract) by joining the table to itself:
SELECT
a.channel_id,
a.timestamp,
b.timestamp,
a.value - b.value as `difference`
FROM table a
JOIN table b
ON a.channel_id = b.channel_id and a.timestamp <> b.timestamp and a.value > b.value
GROUP BY a.channel_id
ORDER BY a.channel_id
You can use a "correlated subquery" for this as seen below (also see this demo). When MySQL implements window functions such a LEAD() you could use those instead.
MySQL 5.6 Schema Setup:
CREATE TABLE Table1
(`id` int, `channel_id` int, `timestamp` bigint, `value` int)
;
INSERT INTO Table1
(`id`, `channel_id`, `timestamp`, `value`)
VALUES
(4515, 7, 1519771680000, 7777),
(4518, 8, 1519772160000, 6666),
(4520, 7, 1519772340000, 8888)
;
Query 1:
select
id
, channel_id
, timestamp
, value
, nxt_value
, nxt_value - value as diff
from (
select
t1.id
, t1.channel_id
, t1.timestamp
, t1.value
, (select value from table1 as t2
where t2.channel_id = t1.channel_id
and t2.timestamp > t1.timestamp
order by t2.timestamp
limit 1) nxt_value
from table1 as t1
) as d
Results:
| id | channel_id | timestamp | value | nxt_value | diff |
|------|------------|---------------|-------|-----------|--------|
| 4515 | 7 | 1519771680000 | 7777 | 8888 | 1111 |
| 4518 | 8 | 1519772160000 | 6666 | (null) | (null) |
| 4520 | 7 | 1519772340000 | 8888 | (null) | (null) |
Starting from MySQL 8, you can use window functions, in case of which your query would look like this:
SELECT
id, channel_id, timestamp, value,
value - LAG(value, 1, 0) OVER (PARTITION BY channel_id ORDER BY timestamp) difference
FROM my_table
thanks for all your support. I tried a lot and created "my" solution based on a stored procedure. It is not as performant as it could be but it delivers the required values.
The code is running in a loop with a max size of repetitions in the script execution to avoid an endless step :)
#Auswahl größer CH10-Wert
set #var_max_ch10vz =
(
select max(data.timestamp)
from volkszaehler.data
where data.channel_id=10
)
;
#Auswahl kleinster offener Wert aus SBFSPOT
set #var_min_sbfspot =
(
select min(data.timestamp_unix*1000)
from sbfspot_u.data
where
data.timestamp_vzjoin is null
and data.timestamp_unix >1522096327
and data.timestamp_unix*1000 < #var_max_ch10vz
)
;
#Abgleich gegen VZ von unten
set #var_max_vz =
(
select min(data.timestamp)
from volkszaehler.data
where data.channel_id=10 and data.timestamp >= #var_min_sbfspot
)
;
#Abgleich gegen VZ von oben
set #var_min_vz =
(
select max(data.timestamp)
from volkszaehler.data
where data.channel_id=10 and data.timestamp <= #var_min_sbfspot
)
;
#Auswahl join Zeitstempel
set #vz_join_timestamp =
(
select tmp.uxtimestamp
from (
select #var_max_vz as uxtimestamp, abs(#var_min_sbfspot-#var_max_vz) as diff
UNION
select #var_min_vz as uxtimestamp, abs(#var_min_sbfspot-#var_min_vz) as diff
) tmp
order by tmp.diff asc
limit 1
)
;
I have 3 columns production number(int) , op number(int) and value(float). No column is distinct by itself. I need to look for the values <= 0 and display everything that's within that production number(int)
Example :
PO# | OP# | values
5247 | 100 | 12.0
5247 | 200 | 22.0
5247 | 300 | -12.0
5247 | 400 | 52.0
6328 | 100 | 11.0
6328 | 300 | 55.0
I need to get these two rows
5247, 300 , -12.0 and
5247, 400 , 52.0
not any other rows. How do I do that?
Just another guess to improve #DarshanMehta query:
http://sqlfiddle.com/#!9/0a3567/2
SELECT t.*
FROM prodOps t
INNER JOIN
(SELECT po, op
FROM prodOps
WHERE `values` < 0) f
ON t.OP >= f.op AND f.po=t.po
but what will happen if you have several records with negative values?
Check this fiddle:
http://sqlfiddle.com/#!9/138f44/1
and guess-solution is:
SELECT t.*
FROM prodOps t
INNER JOIN
(SELECT po, MAX(op) op
FROM prodOps
WHERE `values` < 0
GROUP BY po) f
ON t.OP >= f.op AND f.po=t.po
But I would suggest you to rethink the problem and redesign table and refactor your app. All this way goes to nowhere.
using exists()
select *
from t
where exists (
select 1
from t as i
where t.PO = i.PO
and t.OP >= i.OP
and i.value < 0
)
If I understand your requiremetns
Select A.*
From YourTable A
Join (
Select [PO#]
,min([Values]) as minV
,max([Values]) as maxV
From YourTable
Group by [PO#]
Having min([Values])<0
) B
on A.[PO#]=B.[PO#] and A.[Values] in (B.minV,B.maxV)
Returns
PO# OP# values
5247 300 -12
5247 400 52
Can you try the below query:
SELECT *
FROM table t
WHERE t.OP >=
(SELECT MAX(OP)
FROM table
WHERE PO = t.PO AND value < 0);
update
Here's the SQL Fiddle.
Oh, my title is not the best one and as English is not my main language maybe someone can fix that instead of downvoting if they've understood the issue here.
Basically i have two tables - tourneyplayers and results. Tourneyplayers is like a side table which gathers together tournament information across multiple tables - results, tournaments, players etc. I want to check duplicates from the results table over column day1_best, from single tournament and return all the tourneyplayers who have duplicates.
Tourneyplayers contain rows:
Tourneyplayers
tp_id | resultid | tourneyid
1 | 2 | 91
2 | 21 | 91
3 | 29 | 91
4 | 1 | 91
5 | 3 | 92
Results contains rows:
Results:
r_id | day1_best
1 | 3
2 | 1
3 | 4
.. | ..
21 | 1
.. | ..
29 | 2
Now tourney with id = 91 has in total 4 results, with id's 1,2,21 and 29. I want to return values which have duplicates, so currently the result would be
Result
tp_id | resultid | day1_best
1 | 2 | 1
2 | 21 | 1
I tried writing something like this:
SELECT *
FROM tourneyplayers
WHERE resultid
IN (
SELECT r1.r_id
FROM results AS r1
INNER JOIN results AS r2 ON ( r1.day1_best = r2.day1_best )
AND (
r1.r_id <> r2.r_id
)
)
AND tourneyid =91
But in addition to values which had the same day1_best it chose two more which did not have the same. How could i improve my SQL or rewrite it?
First you JOIN both tables, so you know how the data looks like.
SELECT *
FROM tourney_players t
JOIN results r
ON t.`resultid` = r.`r_id`;
Then using the same query you GROUP to see what tourneyid, day1_best combination has multiple rows
SELECT `tourneyid`, `day1_best`, count(*) as total
FROM tourney_players t
JOIN results r
ON t.`resultid` = r.`r_id`
GROUP BY `tourneyid`, `day1_best`;
Finally you use the base JOIN and perform a LEFT JOIN to see what rows has a match and show only those rows.
SELECT t.`tp_id`, r.`r_id`, r.`day1_best`
FROM tourney_players t
JOIN results r
ON t.`resultid` = r.`r_id`
LEFT JOIN (SELECT `tourneyid`, `day1_best`, count(*) as total
FROM tourney_players t
JOIN results r
ON t.`resultid` = r.`r_id`
GROUP BY `tourneyid`, `day1_best`
HAVING count(*) > 1) as filter
ON t.`tourneyid` = filter.`tourneyid`
AND r.`day1_best` = filter.`day1_best`
WHERE filter.`tourneyid` IS NOT NULL;
SQL DEMO
OUTPUT
Please try this :
Select tp.tp_id , tp.resultid ,r.day1_best from (Select * from Tourneyplayers
where tourneyid = 91)as tp inner join (select * from Result day1_best in(select
day1_best from result group by day1_best having count(*)>1 ) )as r on tp.resultid
= r.r_id ;
When executing below query
SELECT `game_turns`.`in_darts`, `game_turns`.`date`, MAX(game_turns.score) AS max_score
FROM `game_turns`
JOIN `games` ON `games`.`id` = `game_turns`.`game_id` AND `games`.`training` = 1
WHERE `game_turns`.`uid` = 2
AND `game_turns`.`out` = 1
AND `game_turns`.`in_darts` = 3
ORDER BY `game_turns`.`score` DESC
LIMIT 1
I get the max score for that user id (uid) and out in 3 darts, but the rest (date) is wrong.
Fields are
Score Uid GameID Score out in_darts date
121 2 4 8 1 3 2015-07-21 13:52:12
8465 2 142 100 1 3 2015-09-05 19:46:29
It returns the score 100 from row ID 8465 but the rest is from row ID 121
I have googled it and came on some Stackoverflow results saying that I should use ORDER BY and LIMIT 1, but looks like it aint working for me.
Order by Date also didn't do the trick.
A simple order by and limit should do what you want:
SELECT gt.`in_darts`, gt.`date`, gt.score
FROM `game_turns` gt JOIN
`games` g
ON g.`id` = gt.`game_id` AND g.`training` = 1
WHERE gt.`uid` = 2 AND gt.`out` = 1 AND gt.`in_darts` = 3
ORDER BY gt.`score` DESC
LIMIT 1;
There is no need for aggregation.
If seeking a solution that would work for multiple UID's then aggregation becomes useful - via a subquery.
SQL Fiddle
MySQL 5.6 Schema Setup:
CREATE TABLE Table1
(`Score_A` int, `Uid` int, `GameID` int, `Score_B` int, `out` int, `in_darts` int, `date` datetime)
;
INSERT INTO Table1
(`Score_A`, `Uid`, `GameID`, `Score_B`, `out`, `in_darts`, `date`)
VALUES
(121, 2, 4, 8, 1, 3, '2015-07-21 13:52:12'),
(8465, 2, 142, 100, 1, 3, '2015-09-05 19:46:29')
;
Query 1:
SELECT
t.*
FROM table1 t
INNER JOIN (
SELECT Uid, max(Score_B) as Score_B
FROM table1
GROUP BY uid
) msb ON t.Uid = msb.Uid and t.Score_B = msb.Score_B
Results:
| Score_A | Uid | GameID | Score_B | out | in_darts | date |
|---------|-----|--------|---------|-----|----------|-----------------------------|
| 8465 | 2 | 142 | 100 | 1 | 3 | September, 05 2015 19:46:29 |
I'll try to make this clear.
I need to select a specific row and one row previous relative from that selected row and one row next relative from that selected row without using id's. Is this possible? Previous and next one, in short.
The reason why I can't (maybe I just don't know how) use id's, is because they are not in sequential order. They have gaps as you can see from this rather immature and random example.
TABLE <-the name of the table
+----+----------------------+-------+
| id | name | value |
+----+----------------------+-------+
| 1 | some_name | asf |
+----+----------------------+-------+
| 4 | hello | A3r |
+----+----------------------+-------+
| 5 | how_do_you_do | HR5 |
+----+----------------------+-------+
| 8 | not_bad | 00D |
+----+----------------------+-------+
| 12 | i_like_women | lla |
+----+----------------------+-------+
| 13 | are_you_serious | 1Ha |
+----+----------------------+-------+
| 15 | nah_i_kid | Ad4 |
+----+----------------------+-------+
| 17 | it_is_just_the_boobs | Zc5 |
+----+----------------------+-------+
| 18 | thank_god | 102 |
+----+----------------------+-------+
| 44 | no_kidding | jjy |
+----+----------------------+-------+
First, I need to select one row based on specific value from one of its column. I know how to do that:
SELECT `value`
FROM `TABLE`
WHERE name = 'i_like_women'
This will select one row with id 12 with the value lla.
What I need is to select another at least two rows: one with the name 'not_bad' and one with the name 'are_you_serious' without specifying it. Or, in other words, previous and next one relative to this selected one.
In short, three rows should be selected based on one value. I'm new to MySQL, as you can guess.
Thanks for your time and attention. Enjoy helping me.
Here is the query which will return all three records.
SELECT *
FROM `TABLE`
WHERE id >= (
SELECT id
FROM `TABLE`
WHERE id < (SELECT id FROM `TABLE` WHERE name = 'i_like_women')
ORDER BY id DESC
LIMIT 1
)
ORDER BY id ASC
LIMIT 3
The simplest way to do this is to exploit the fact that, although not continuous, your ids are in ascending order.
For example:
SELECT * FROM Table WHERE id = 8
UNION
--Select the first item less than 8
SELECT * FROM Table WHERE id = (SELECT MAX(id) FROM Table WHERE id < 8)
UNION
--select the first item greater than 8
SELECT * FROM Table WHERE id = (SELECT MIN(id) FROM Table WHERE id > 8)
If you only know the string, then:
DECLARE _id INT
SELECT _id = id FROM Table WHERE value = 'i_like_women'
Then you can simply feed this _id into the above query, instead of 8.
Note you don't need to use ` to demarcate the table and column names.
The one before can be retrieved with:
SELECT `value`
FROM `TABLE`
WHERE id < (SELECT id FROM `TABLE` WHERE name = 'i_like_women')
ORDER BY id DESC
LIMIT 1
You can do the opposite query to find the next one
this query is working fine on first and last record as well
SELECT * FROM `products` WHERE `ProductId` = (SELECT MAX(ProductId) FROM `products` WHERE ProductId < 1) AND SubCategoryId=1
UNION
SELECT * FROM `products` WHERE `ProductId` = (SELECT MIN(ProductId) FROM `products` WHERE ProductId > 1) AND SubCategoryId=1
UNION
SELECT * FROM `products` WHERE `ProductId` = (SELECT MIN(ProductId) FROM `products` WHERE ProductId < 1) AND SubCategoryId=1
UNION
SELECT * FROM `products` WHERE `ProductId` = (SELECT MAX(ProductId) FROM `products` WHERE ProductId > 1) AND SubCategoryId=1
ORDER BY ProductId ASC
i Hope this will solve your Problem :)
I have found better and easy answer for the below question(finding next and previous row ),
$neighbors = $this->WorkDescription->find('neighbors',array('field' => 'id', 'value' => 3));
it will gives output like this-
Array
(
[prev] => Array
(
[WorkDescription] => Array
(
[id] => 1
[title] => Twenty
[ter_id] => 1
[cat_id] => 4
[writer] => abk
[director] => Dir
[producer] => pro
)
)
[next] => Array
(
[WorkDescription] => Array
(
[id] => 3
[title] => The Piper
[ter_id] => 1
[cat_id] => 3
[writer] => abk
[director] => Dir
[producer] => pro
)
)
)