Список выбора SQL должен быть исправлен во время написания запроса. Вы не можете сделать SQL, который автоматически расширяет свои столбцы на основе найденных данных.
Но ваш запрос является общим, он называется сводной таблицей или таблицей кросс-таблицы.
Единственное решение состоит в том, чтобы сделать это несколькими этапами:
Это справедливо для всех баз данных SQL, а не только для MySQL.
См. MySQL pivot row в динамическое число столбцов для высокопринятого решения для создания запроса сводной таблицы в MySQL.
Я не голосую ваш вопрос как дубликат этого вопроса, потому что ваш запрос также включает transport_id, что сделает решение для запросов немного другим. Но вы должны прочитать о других решениях с поворотным столом.
Вы можете решить проблему, дважды используя адаптированный бинарный поиск на Keys
, чтобы найти индексы, которые связывали интересующий диапазон в коллекции Keys
.
Поскольку IList<T>
не предлагайте бинарные средства поиска, которые вам нужно написать самостоятельно. К счастью, есть также возможность украсть готовые реализации из . Как выполнить бинарный поиск в IList .
Вот адаптированная версия, чтобы найти нижнюю границу:
public static int LowerBound<T>(this IList<T> list, T value, IComparer<T> comparer = null)
{
if (list == null)
throw new ArgumentNullException("list");
comparer = comparer ?? Comparer<T>.Default;
int lower = 0, upper = list.Count - 1;
while (lower <= upper)
{
int middle = lower + (upper - lower) / 2;
int comparisonResult = comparer.Compare(value, list[middle]);
// slightly adapted here
if (comparisonResult <= 0)
upper = middle - 1;
else
lower = middle + 1;
}
return lower;
}
Чтобы реализовать UpperBound
, просто измените
if (comparisonResult <= 0)
на
if (comparisonResult < 0)
. Теперь это тривиально:
var low = set.Keys.LowerBound(value);
var high = set.Keys.UpperBound(value);
// These extra comparisons are required because the adapted binary search
// does not tell us if it actually found the needle. They could be rolled
// into the methods themselves, but this would require another out parameter.
if (set.Keys[low] != value) ++low;
if (set.Keys[high] != value) --high;
if (low <= high) /* remove keys in the range [low, high] */
Когда список отсортирован, вы можете использовать двоичный поиск для определения конечных точек вашего интервала. Наихудшим случаем будет O (log n).
Я задавался вопросом, почему SortedList<TKey, TValue>
не предоставляет BinarySearch
, когда он уже отсортирован ключами. Он также использует сам метод (f.e. In IndexOf
), но используемый массив является частным полем.
Итак, я попытался создать для этого метод расширения, посмотрите:
public static class SortedListExtensions
{
public static int BinarySearch<TKey, TValue>(this SortedList<TKey, TValue> sortedList, TKey keyToFind, IComparer<TKey> comparer = null)
{
// need to create an array because SortedList.keys is a private array
var keys = sortedList.Keys;
TKey[] keyArray = new TKey[keys.Count];
for (int i = 0; i < keyArray.Length; i++)
keyArray[i] = keys[i];
if(comparer == null) comparer = Comparer<TKey>.Default;
int index = Array.BinarySearch<TKey>(keyArray, keyToFind, comparer);
return index;
}
public static IEnumerable<TKey> GetKeyRangeBetween<TKey, TValue>(this SortedList<TKey, TValue> sortedList, TKey low, TKey high, IComparer<TKey> comparer = null)
{
int lowIndex = sortedList.BinarySearch(low, comparer);
if (lowIndex < 0)
{
// list doesn't contain the key, find nearest behind
// If not found, BinarySearch returns the complement of the index
lowIndex = ~lowIndex;
}
int highIndex = sortedList.BinarySearch(high, comparer);
if (highIndex < 0)
{
// list doesn't contain the key, find nearest before
// If not found, BinarySearch returns the complement of the index
highIndex = ~highIndex - 1;
}
var keys = sortedList.Keys;
for (int i = lowIndex; i < highIndex; i++)
{
yield return keys[i];
}
}
}
Создайте образец SortedList
:
DateTime start = DateTime.Today.AddDays(-50);
var sortedList = new SortedList<DateTime, string>();
for(int i = 0; i < 50; i+=2)
{
var dt = start.AddDays(i);
sortedList.Add(dt, string.Format("Date #{0}: {1}", i, dt.ToShortDateString()));
}
DateTime low = start.AddDays(1); // is not in the SortedList which contains only every second day
DateTime high = start.AddDays(10);
Теперь вы можете использовать метод расширения выше, чтобы получить диапазон ключей между низким и высоким ключом:
IEnumerable<DateTime> dateRange = sortedList.GetKeyRangeBetween(low, high).ToList();
Результат:
04/04/2014
04/06/2014
04/08/2014
04/10/2014
Обратите внимание, что это построенный с нуля и не протестированный, но он может дать вам идею.