英文:
sorting multiple fields of the same table
问题
我有一个排序函数,根据指定的表格,应该按我需要的顺序筛选数据。
public function getOrderBy(): array
{
$sortOrder = in_array(
$this->fieldSort,
['price_desc'],
true
) ? SORT_DESC : SORT_ASC;
$by = in_array(
$this->fieldSort,
['price_asc', 'price_desc'],
true
) ? 'hotel_price.price' : null;
return $by ? [$by => $sortOrder] : [];
}
$query = self::getActive();
$query->select([self::tableName() . '.*']);
if ($validate && !empty($filter->getOrderBy())) {
$query->joinWith('hotelPrice');
$query->orderBy($filter->getOrderBy());
}
表格看起来像这样:
结果是,按 price_asc 排序工作正常,我得到了从最小到最大的值。
但是 price_desc 有一些问题。它进行排序,但不完全正确。
事实上,这里从表中获取一个数组,假设对于 hotel_id = 1,它具有值 2 和 3。排序按照数组的第一个元素进行。对于 price_asc,这是正常的,但对于 price_desc,您需要确保排序从数组的最后一个元素开始。
但我仍然不知道如何做到这一点,我需要在排序函数本身中做这个,还是我需要深入挖掘?
更新
这是一个值的数组,比如现在 hotel_id = 5,它有一个价格值的数组 [1, 2, 3],现在 asc 和 desc 的排序都从数组的第一个元素开始。而我需要为 desc 从最后开始。
到目前为止,我唯一的想法是将 id 列与 price_range 列一起排序。
但是如何将这样的值传递给 SQL 呢?应该是这样的:
$by = 'hotel_price.id', 'hotel_price.price';
如果我对 id 进行 DESC 排序,那么最后一个值将出现在表格的第一位置,数组将变成这样的价格值 [3, 2, 1]。
我将再次尝试解释,我的代码中的函数执行以下操作:
SELECT DISTINCT hotels.*, FROM hotel LEFT JOIN hotel_price ON hotels.id = hotel_price.hotel_id WHERE (hotels.status=1) ORDER BY hotel_price.price DESC
我需要使其对 id 以及 price 应用排序。
响应应该是类似于:
ORDER BY hotel_price.id DESC, hotel_price.price DESC
需要应用双重排序。
我知道这对于不同的表格来说是完全可能的,但是对于同一个表格的不同字段是否可能呢?
在查询中,我会输出类似于这样的内容,但最终只有第一个字段被排序:
ORDER BY hotel_price.id DESC, hotel_price.price DESC
===========================
- 我有一个如此的表格
我在这里需要两个字段,id 和 price。
当我只对其中一个价格字段或一个 id 字段进行排序时,一切正常。
- 这是我运行的查询
SELECT DISTINCT hotels.*, FROM hotel LEFT JOIN hotel_price ON hotels.id = hotel_price.hotel_id WHERE (hotels.status=1) ORDER BY hotel_price.id DESC, hotel_price.price DESC
- 我期望首先我的表格将按 id 排序,它会看起来像这样
并且价格字段的第二个排序将应用于按 id 排序的此表格
-
但最终,只有对 id 的第一个排序对我起作用,也就是说,表格变成了与最后一个截图中相同的样子,但对价格的第二个排序未应用于它。
-
附加
如果我发送一个像这样的请求
SELECT DISTINCT hotels.*, FROM hotel LEFT JOIN hotel_price ON hotels.id = hotel_price.hotel_id WHERE (hotels.status=1) ORDER BY hotel_price.price DESC, hotel_price.id DESC
也就是说,首先我会尝试按价格排序,然后按 ID 排序,反之亦然,它会按价格排序,但按 ID 排序却不见了。
简而言之,排序始终只应用于第一个字段,尽管我在请求中发送了两个字段。
===========================
我将我的表格添加到了帖子中。在前端显示时,让我们假设我们选择了 ID 为 5 的酒店,它具有一个价格数组 [1, 2, 3]。
当我在排序酒店之前按价格排序时,总是会按照他获取的第一个值来排序。在这种情况下,它是 1。
当我按 ASC 排序时,一切显示正常,但当我按 DESC 排序时,仍然会按照排序的第一个价格值,然后排序看起来不正确。
也就是说,要按 DESC 排序,我需要以相反的顺序从表中获取价格数组 [3, 2, 1],以便获取最大值进行排序。
英文:
I have a sorting function that, according to the specified table, should filter the data in the order I need..
public function getOrderBy(): array
{
$sortOrder = in_array(
$this->fieldSort,
['price_desc'],
true
) ? SORT_DESC : SORT_ASC;
$by = in_array(
$this->fieldSort,
['price_asc', 'price_desc'],
true
) ? 'hotel_price.price' : null;
return $by ? [$by => $sortOrder] : [];
}
$query = self::getActive();
$query->select([self::tableName() . '.*']);
if ($validate && !empty($filter->getOrderBy())) {
$query->joinWith('hotelPrice');
$query->orderBy($filter->getOrderBy());
}
The table looks like this:
As a result, sorting for price_asc works as it should, I get values from minimum to large.
But there are some problems with price_desc. It sorts, but not quite right.
The fact is that here an array is taken from the table, let's say for hotel_id = 1, it has values 2 and 3. And sorting works by the first element of the array. For price_asc, this is normal, but for price_desc, you need to make sure that sorting starts from the last element of the array.
But I still can’t figure out how to do this, do I need to do this in the sort function itself, or will I have to dig deeper?
UPDATE
It is the array of values, let's say now hotel_id = 5, that has an array of price values [1, 2, 3], and now sorting for asc and desc starts from the first element of the array. And I need for desc to start from the last.
The only idea I have so far is to sort the id column along with the price_range column.
But how do I pass such a value to SQL. Should be something like:
$by = 'hotel_price.id', 'hotel_price.price';
If I make DESC for the id, then the last value will be in the first place in the table and the array will look like this price values [3, 2, 1]
I'll try to explain again, my function, which is in the code, does the following:
>SELECT DISTINCT hotels.*, FROM hotel LEFT JOIN hotel_price ON hotels.id = hotel_price.hotel_id WHERE (hotels.status=1) ORDER BY hotel_price.price DESC
I need to make it so that sorting is applied to id along with price.
And the response should be something like:
>ORDER BY hotel_price.id DESC, hotel_price.price DESC
Need to apply double sorting.
I know it's quite possible for different tables, but is it possible to do it for one table but for different fields?
I output something like this in the query, but in the end only the first field is sorted:
>ORDER BY hotel_price.id DESC, hotel_price.price DESC
===========================
- I have a table like this
I need two fields here, id and price.
When I apply sorting by only one price field or one id field, then everything works.
- Here is the query I am running
>SELECT DISTINCT hotels.*, FROM hotel LEFT JOIN hotel_price ON hotels.id = hotel_price.hotel_id WHERE (hotels.status=1) ORDER BY hotel_price.id DESC, hotel_price.price DESC
- I expect that at first my table will be sorted by id and it will look like this
And the second sorting by the price field will be applied to this sorted table by id
-
But in the end, only the first sort by id works for me, that is, the table becomes the same as in the last screenshot, but the second sort by price is not applied to it.
-
Addition
If I send a request like this
>SELECT DISTINCT hotels.*, FROM hotel LEFT JOIN hotel_price ON hotels.id = hotel_price.hotel_id WHERE (hotels.status=1) ORDER BY hotel_price.price DESC, hotel_price.id DESC
Ie, first I'll try to sort by price, then by ID, then on the contrary, it sorts by price, but by id it's gone.
In short, sorting is always applied only to the first field, although I send two in the request.
===========================
I added my table to the post. And when displaying on the frontend, let's say we take a hotel with ID 5, and it has an array with prices [1, 2, 3].
And when I sort hotels by price before hotels are always sorted by price where he takes the first value. In this case it's 1.
When I sort by ASC, everything is displayed correctly, but when I sort by DESC, the first price value is still taken in the sort, and then the sort looks incorrect.
That is, to sort by DESC, I need to get the array with prices from the table in the reverse order [3, 2, 1], so that the largest value is taken for sorting.
答案1
得分: 3
你可能需要修改getOrderBy
函数,在降序排序时按数组的最后一个元素进行排序。
尝试使用array_reverse
函数:
public function getOrderBy(): array
{
$sortOrder = in_array(
$this->fieldSort,
['price_desc'],
true
) ? SORT_DESC : SORT_ASC;
$by = in_array(
$this->fieldSort,
['price_asc', 'price_desc'],
true
) ? 'hotel_price.price' : null;
if ($sortOrder === SORT_DESC) {
$by = "SUBSTRING_INDEX(hotel_price.price, ',', -1)";
}
return $by ? [$by => $sortOrder] : [];
}
英文:
You might need to modify the getOrderBy function to sort by the last element of the array when sorting in descending order
Try this using the array_reverse function :
public function getOrderBy(): array
{
$sortOrder = in_array(
$this->fieldSort,
['price_desc'],
true
) ? SORT_DESC : SORT_ASC;
$by = in_array(
$this->fieldSort,
['price_asc', 'price_desc'],
true
) ? 'hotel_price.price' : null;
if ($sortOrder === SORT_DESC) {
$by = "SUBSTRING_INDEX(hotel_price.price, ',', -1)";
}
return $by ? [$by => $sortOrder] : [];
}
答案2
得分: 1
如果我正确理解问题,那么需要进行第二次排序。
你的第一个标准将是价格:
public function getOrderBy(): array
{
$sortOrder = in_array(
$this->fieldSort,
['price_desc'],
true
) ? SORT_DESC : SORT_ASC;
$by = in_array(
$this->fieldSort,
['price_asc', 'price_desc'],
true
) ? 'hotel_price.price' : null;
return $by ? [$by => $sortOrder, 'hotel_price.id' => SORT_ASC] : ['hotel_price.id' => SORT_ASC];
}
而这是另一种方式:
public function getOrderBy(): array
{
$sortOrder = in_array(
$this->fieldSort,
['price_desc'],
true
) ? SORT_DESC : SORT_ASC;
$by = in_array(
$this->fieldSort,
['price_asc', 'price_desc'],
true
) ? 'hotel_price.price' : null;
return $by ? ['hotel_price.id' => SORT_ASC, $by => $sortOrder] : ['hotel_price.id' => SORT_ASC];
}
编辑
基本上你有两种排序标准。重要的是要理解,当我们对 r1 和 r2 记录进行排序时,然后确定这些记录的顺序,但在结果集中,r1 和 r2 具有单一顺序。我们不对单个字段进行排序;我们基于某些排序标准对整个记录进行排序。"ORDER BY foo desc, bar desc" 换言之意味着以下内容:
如果 r1.foo ≠ r2.foo,那么 r1 和 r2 的顺序由 foo 的降序确定;否则 r1 和 r2 的顺序由 bar 的降序确定。
我为此目的构建了一个测试案例。
数据构建
create table hotels(
id int primary key auto_increment,
price int not null,
hotel_id int not null
);
insert into hotels(price, hotel_id)
values
(2, 1),
(3, 1),
(1, 3),
(2, 3),
(2, 4),
(3, 4),
(1, 5),
(2, 5),
(3, 5),
(2, 6);
Fiddle: http://sqlfiddle.com/#!9/99f449/5
下面我讨论了几种情况,请查看代码,尤其是注释部分。
英文:
If I understand the question correctly, then a second sort is needed.
This is how your first criteria would be price:
public function getOrderBy(): array
{
$sortOrder = in_array(
$this->fieldSort,
['price_desc'],
true
) ? SORT_DESC : SORT_ASC;
$by = in_array(
$this->fieldSort,
['price_asc', 'price_desc'],
true
) ? 'hotel_price.price' : null;
return $by ? [$by => $sortOrder, 'hotel_price.id' => SORT_ASC] : ['hotel_price.id' => SORT_ASC];
}
And this is the other way around:
public function getOrderBy(): array
{
$sortOrder = in_array(
$this->fieldSort,
['price_desc'],
true
) ? SORT_DESC : SORT_ASC;
$by = in_array(
$this->fieldSort,
['price_asc', 'price_desc'],
true
) ? 'hotel_price.price' : null;
return $by ? ['hotel_price.id' => SORT_ASC, $by => $sortOrder] : ['hotel_price.id' => SORT_ASC];
}
EDIT
Basically you have two sort criterias. It's important to understand that when we sort the r1 and r2 records, then we determine the order of these records, but in the result set r1 and r2 have a single order. We do not order individual fields; we order whole records based on some sort criterias. A sort criteria of
ORDER BY foo desc, bar desc
in plain words means the following:
**
If r1.foo <> r2.foo then the order of r1 and r2 is determined by foo descendingly
else the order of r1 and r2 is determined by bar descendingly
**
I have constructed a test case for this purpose.
Data build
create table hotels(
id int primary key auto_increment,
price int not null,
hotel_id int not null
);
insert into hotels(price, hotel_id)
values
(2, 1),
(3, 1),
(1, 3),
(2, 3),
(2, 4),
(3, 4),
(1, 5),
(2, 5),
(3, 5),
(2, 6);
Fiddle: http://sqlfiddle.com/#!9/99f449/5
Below I discuss several cases, please look into the code and especially to the comments.
order by id desc, price desc
/*
This orders by id, price
We expect for any r1, r2 records that
- if r1.id > r2.id, then r1 is before r2
- if r1.id = r2.id, then we order them by price
- if r1.price > r2.price then r1 is before r2
- if r1.price = r2.price then our sort criteria does not determine their order
- if r1.price < r2.price then r1 is after r2
- if r2.id < r2.id, then r1 is after r2
As we can see above, r1 and r2 have a single order, which is determined by id and it would only be
determined by price if r1.id = r2.id, so, if there is a tie in the order of ids, then price is
the tiebreaker. Yet, the id field is unique (it's even a primary key), so we never end up using
price as an order criteria
*/
select *
from hotels
order by id desc, price desc;
order by price desc, id desc
/*
This orders by price, id
We expect for any r1, r2 records that
- if r1.price > r2.price, then r1 is before r2
- if r1.price = r2.price, then
- if r1.id > r2.id, then r1 is before r2
- if r1.id = r2.id, then our sort criteria does not determine their order
- if r1.id < r2.id, then r1 is after r2
- if r1.price < r2.price, then r1 is after r2
So, our first sort criteria is price and our records are ordered by price, except when their prices
are equal, in which case we break the tie by our second sorting criteria
*/
select *
from hotels
order by price desc, id desc;
order by hotel_id desc, price desc
/*
This orders by hotel_id, price
We expect for any r1, r2 records that
- if r1.hotel_id > r2.hotel_id, then r1 is before r2
- if r1.hotel_id = r2.hotel_id, then we order them by price
- if r1.price > r2.price then r1 is before r2
- if r1.price = r2.price then our sort criteria does not determine their order
- if r1.price < r2.price then r1 is after r2
- if r2.hotel_id < r2.hotel_id, then r1 is after r2
As we can see above, r1 and r2 have a single order, which is determined by hotel_id and it would
only be determined by price if r1.hotel_id = r2.hotel_id, so, if there is a tie in the order of
hotel ids, then price is the tiebreaker.
*/
select *
from hotels
order by hotel_id desc, price desc;
order by price desc, hotel_id desc
/*
This orders by price, hotel_id
We expect for any r1, r2 records that
- if r1.price > r2.price, then r1 is before r2
- if r1.price = r2.price, then
- if r1.hotel_id > r2.hotel_id, then r1 is before r2
- if r1.hotel_id = r2.hotel_id, then our sort criteria does not determine their order
- if r1.hotel_id < r2.hotel_id, then r1 is after r2
- if r1.price < r2.price, then r1 is after r2
So, our first sort criteria is price and our records are ordered by price, except when their prices
are equal, in which case we break the tie by our second sorting criteria
*/
select *
from hotels
order by price desc, hotel_id desc;
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论