Последний день месяца с изюминкой в ​​SQLPLUS - PullRequest
2 голосов
/ 15 июля 2009

Буду признателен за небольшую помощь специалиста, пожалуйста.

в операторе SQL SELECT. Я пытаюсь получить последний день с данными за месяц за последний год. Например, я легко могу получить последний день каждого месяца и присоединить его к своей таблице данных, но проблема в том, что если в последний день месяца нет данных, то нет возвращаемых данных. Мне нужно, чтобы SELECT возвратил последний день с данными за месяц.

Это, вероятно, легко сделать, но, честно говоря, мой мозг пердит начинает болеть.

Я приложил нижеприведенный выбор, который работает для возврата данных только за последний день месяца за последние 12 месяцев.

Заранее спасибо за помощь!

SELECT fd.cust_id,fd.server_name,fd.instance_name,
    TRUNC(fd.coll_date) AS coll_date,fd.column_name
FROM super_table fd,
    (SELECT TRUNC(daterange,'MM')-1 first_of_month
    FROM (
    select TRUNC(sysdate-365,'MM') + level as DateRange
    from    dual
    connect by level<=365)
    GROUP BY TRUNC(daterange,'MM')) fom
WHERE fd.cust_id = :CUST_ID
AND fd.coll_date > SYSDATE-400
AND TRUNC(fd.coll_date) = fom.first_of_month
GROUP BY fd.cust_id,fd.server_name,fd.instance_name,
    TRUNC(fd.coll_date),fd.column_name
ORDER BY fd.server_name,fd.instance_name,TRUNC(fd.coll_date)

Ответы [ 3 ]

2 голосов
/ 16 июля 2009

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

SELECT MAX(coll_date) AS last_day_of_month
    FROM Super_Table AS fd
    GROUP BY YEAR(coll_date) * 100 + MONTH(coll_date);

Предполагается, что существуют функции YEAR () и MONTH () для извлечения года и месяца из даты в виде целочисленного значения. Понятно, что это не ограничивает диапазон дат - вы тоже можете это сделать. Если у вас нет функций в Oracle, то вы делаете какие-то манипуляции, чтобы получить эквивалентный результат.

Использование информации от Rhose (спасибо):

SELECT MAX(coll_date) AS last_day_of_month
    FROM Super_Table AS fd
    GROUP BY TO_CHAR(coll_date, 'YYYYMM');

Достигается тот же чистый результат: все даты одного календарного месяца объединяются в группу, а затем определяется максимальное значение, присутствующее в этой группе.

1 голос
/ 16 июля 2009

Вот другой подход, если поддерживается ANSI row_number():

with RevDayRanked(itemDate,rn) as (
  select
    cast(coll_date as date),
    row_number() over (
      partition by datediff(month,coll_date,'2000-01-01') -- rewrite datediff as needed for your platform
      order by coll_date desc
    )
  from super_table
)
  select itemDate
  from RevDayRanked
  where rn = 1;

Строки с номером 1 будут недетерминированно выбраны среди строк на последнюю активную дату месяца, поэтому вам не нужно различать. Если вы хотите получить информацию из таблицы для всех строк в эти даты, используйте rank() по дням вместо row_number() по значениям coll_date, поэтому значение 1 отображается для любой строки в последнюю активную дату месяца и выберите дополнительные столбцы, которые вам нужны:

with RevDayRanked(cust_id, server_name, coll_date, rk) as (
  select
    cust_id, server_name, coll_date,
    rank() over (
      partition by datediff(month,coll_date,'2000-01-01')
      order by cast(coll_date as date) desc
    )
  from super_table
)
  select cust_id, server_name, coll_date
  from RevDayRanked
  where rk = 1;

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

select
  cust_id, server_name, coll_date
from super_table as ST1
where not exists (
  select *
  from super_table as ST2
  where datediff(month,ST1.coll_date,ST2.coll_date) = 0
  and cast(ST2.coll_date as date) > cast(ST1.coll_date as date)
)

Если вам приходится много заниматься такими вещами, посмотрите, можете ли вы создать индекс по вычисляемым столбцам, которые содержат cast(coll_date as date), и индикатор месяца, например datediff(month,'2001-01-01',coll_date). Это сделает больше из предикатов SARG.

1 голос
/ 16 июля 2009

Сложив вышеперечисленные кусочки, вам подойдет что-нибудь подобное?

 SELECT fd.cust_id,
       fd.server_name,
       fd.instance_name,
       TRUNC(fd.coll_date) AS coll_date,
       fd.column_name
  FROM super_table fd,
 WHERE fd.cust_id = :CUST_ID
   AND TRUNC(fd.coll_date) IN (
                                SELECT MAX(TRUNC(coll_date))
                                  FROM super_table
                                 WHERE coll_date > SYSDATE - 400
                                   AND cust_id = :CUST_ID
                                 GROUP BY TO_CHAR(coll_date,'YYYYMM')
                              )
 GROUP BY fd.cust_id,fd.server_name,fd.instance_name,TRUNC(fd.coll_date),fd.column_name
 ORDER BY fd.server_name,fd.instance_name,TRUNC(fd.coll_date)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...