SQL запрос, чтобы получить последнюю цену из данных? - PullRequest
0 голосов
/ 15 февраля 2020

У меня есть эти данные в моей таблице Oracle:

enter image description here

В этой таблице у меня есть несколько продуктов с product_id и ценой в определенное время.

Я хочу написать запрос SQL, чтобы получить последнюю цену каждого продукта на основе столбца TIME (Oracle - Длинный тип).

Но вот в чем проблема, для нашей таблицы отслеживание цен на продукт начинается в 23:00 предыдущей даты.

Требуется вывод: когда я передал 2-De c -2019 в качестве параметра в запрос SQL

enter image description here

Как это сделать с помощью запроса SQL? Или мне нужно написать хранимую процедуру для нее, основываясь на условии if и else?

Заранее спасибо.

Ответы [ 4 ]

2 голосов
/ 15 февраля 2020

Мы можем попробовать использовать ROW_NUMBER для этого требования:

WITH cte AS (
    SELECT t.*, ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY time DESC) rn
    FROM yourTable t
    WHERE system_date = date '2019-12-19'
)

SELECT local_date, system_date, currency, product_id, time, current_price
FROM cte
WHERE rn = 1;
1 голос
/ 17 февраля 2020

Прежде всего вы храните дату и время отдельно, что делает работу с датой и временем более сложной, чем необходимо.

Один способ объединить два:

local_date + interval '1' second * (to_number(substr(time, 5, 2)) +
                                    to_number(substr(time, 3, 2)) * 60 +
                                    to_number(substr(time, 1, 2)) * 3600)

Другой :

to_date(to_char(local_date, 'yyyymmdd') || time, 'yyyymmddhh24miss')

С этим датой и временем вы хотите что-то вроде

where the_time >= timestamp '2019-12-01 23:00:00' and the_time < date '2019-12-03'

Для какой-то еще неизвестной даты, которая будет предоставлена ​​при выполнении запроса:

where the_time >= :date - interval '1' hour and the_time < :date + interval '1' day

Для текущего дня, предположим, что в таблице нет будущих данных:

where the_time >= trunc(sysdate) - interval '1' hour

Что касается предпочтения более текущей даты-времени, чем более старой, используйте оконную функцию, например, MAX OVER.

with rows_with_datetime as
(
  select
    mytable.*, 
    to_date(to_char(local_date, 'yyyymmdd') || time, 'yyyymmddhh24miss') as dt
  from mytable
)
, two_days_with_maxdatetime as
(
  select
    rows_with_datetime.*,
    max(dt) over (partition by product_id order by dt) as max_dt
  from rows_with_datetime
  where dt >= trunc(sysdate) - interval '1' hour
)
select *
from two_days_with_maxdatetime
where dt = max_dt
order by product_id;

Поскольку вычисляется дата и время, здесь индекс не будет использоваться, и запрос может быть довольно медленным. Таким образом, вам лучше изменить модель данных, чтобы иметь дату и время, которые можно легко индексировать и использовать вместо отдельных даты и времени. Если вы не можете, вы можете применить дополнительное избыточное условие только на одну дату:

where local_date >= trunc(sysdate) - interval '1' day
1 голос
/ 15 февраля 2020

О, понятно. Вы можете использовать два параметра в ORDER BY:

SELECT local_date, system_date, currency, product_id, time, current_price
FROM (SELECT t.*,
             ROW_NUMBER() OVER (PARTITION BY product_id
                                ORDER BY local_date DESC, time DESC
                               ) as seqnum
      FROM t
      WHERE system_date = @parameter
     ) t
WHERE rn = 1;

Я не уверен, действительно ли вам нужна фильтрация по дате. Возможно, вы захотите:

      WHERE system_date <= @parameter

или:

      WHERE system_date < @parameter + interval '1' day

Это позволит вам получить данные за более ранние дни, если нет данных на дату, указанную параметром. Вторая версия также работает, если system_date имеет компонент времени (что разрешено для date типов данных в Oracle).

0 голосов
/ 15 февраля 2020

Вы можете расширить идею из другого ответа (используя ROW_NUMBER), чтобы определить представление , предоставляя только записи с последней ценой в день.

Вы должны предоставить измерения ( product_id и day) в предложении PARTITION BY, которое будет работать для любого продукта и любого дня - выберите только строку с наибольшим временем в день и продуктом .

create view last_price_per_day as 
with last_price as (
select 
  SYSTEM_DATE, PRODUCT_ID, TIME, CURRENT_PRICE,
  row_number() over (partition by PRODUCT_ID, SYSTEM_DATE order by TIME desc) as rn
from tab)
select 
  SYSTEM_DATE, PRODUCT_ID, TIME, CURRENT_PRICE
from last_price
where rn = 1;

Примечание. что этот подход превосходит альтернативные, использующие MAX(TIME) в коррелированном подзапросе, поскольку он отлично работает, даже если имеет место связь (больше строк с одинаковым самым высоким временем).

Если это релевантная топика c, вы может добавить дополнительные столбцы к предложению ORDER BY, например, добавив CURRENT_PRICE DESC, вы получите самую высокую цену из связанных строк.

Хороший чек для такой последней строки Представления должны проверить, являются ли столбцы в PARTITIEN BY и ORDER BY уникальными в таблице.

В вашем случае PRODUCT_ID, SYSTEM_DATE, TIME должен быть уникальным, в противном случае представление не является детерминированным c и вернется при повторном запрашивает разные результаты - что может быть не так, как вы ожидаете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...