Отримайте записи минулого місяця на сервері SQL


77

Я хочу отримати записи минулого місяця на основі моєї таблиці db [член] поля "date_created".

Який sql для цього?

Для роз’яснень, минулого місяця - з 1/8/2009 по 31/8/2009

Якщо сьогодні 01.03.2010, мені потрібно буде отримати записи з 12.01.2009 по 31.12.2009.


2
Чи означає "минулий місяць" "останні 30 днів", "усі дні попереднього місяця" або "всі дні поточного місяця"?
— Cellfish

Найкращий синтаксис також залежить від того, використовуєте ви SQL 2005 (або раніше) із полем DATETIME, або SQL 2008 із полем DATE.
— Ейдилон

4
Що про DATEDIFF(month, date_created, GETDATE()) = 1? Чи не спрацювало б і це?
— Філіп М

Відповіді:


115
SELECT * 
FROM Member
WHERE DATEPART(m, date_created) = DATEPART(m, DATEADD(m, -1, getdate()))
AND DATEPART(yyyy, date_created) = DATEPART(yyyy, DATEADD(m, -1, getdate()))

Потрібно перевірити місяць і рік.


2
у моєму випадку SELECT * FROM Member WHERE DATEPART(m, date_created) = DATEPART(m, DATEADD(m, -1, getdate())) AND DATEPART(yy, date_created) = DATEPART(yy, DATEADD(m, -1, getdate()))працює ідеально
— gofor.net

9
Цей підхід буде ігнорувати будь-які індекси в таблиці та виконувати сканування таблиці (або кластерного індексу) кожного разу, коли він запускається. Чим більша таблиця, тим довше триватиме запит.
— mrdenny

5
Ви повинні використовувати yyyy замість y
— вставте

105

Усі існуючі (робочі) відповіді мають одну з двох проблем:

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

1. Ігноровані індекси:

Здебільшого, коли в стовпці, який шукається, є функція, що викликається (в тому числі неявно, наприклад, for CAST), оптимізатор повинен ігнорувати індекси в стовпці та шукати кожен запис. Ось короткий приклад:

Ми маємо справу з мітками часу, і більшість СУБД, як правило, зберігають цю інформацію як зростаюче певне значення, як правило, longабо BIGINTEGERкількість мілі- / наносекунд. Поточний час таким чином виглядає / зберігається таким чином:

1402401635000000  -- 2014-06-10 12:00:35.000000 GMT

Ви там не бачите значення "Рік" ( '2014'), правда? Насправді є досить складна математика для перекладу вперед і назад. Отже, якщо ви викликаєте будь-яку функцію частини вилучення / дати у пошуковому стовпці, сервер повинен виконати всю цю математику лише для того, щоб з’ясувати, чи можете ви включити її в результати. На невеликих таблицях це не проблема, але із зменшенням відсотка вибраних рядків це стає більшим і більшим стоком. Тоді в цьому випадку ви робите це вдруге, запитуючи про MONTH... ну, ви отримуєте картину.

2. Ненавмисні дані:

В залежності від конкретної версії SQL Server і типів даних стовпців, використовуючи BETWEEN(або аналогічні включно верхньої межі діапазонів: <=) може привести до неправильних даними обраний . По суті, ви потенційно в кінцевому підсумку включаєте дані з опівночі "наступного" дня або виключаючи певну частину записів "поточного" дня.

Що ви повинні робити:

Отже, нам потрібен безпечний для наших даних спосіб, і ми будемо використовувати індекси (якщо це життєздатно). Тоді правильний спосіб має вигляд:

WHERE date_created >= @startOfPreviousMonth AND date_created < @startOfCurrentMonth

Враховуючи, що існує лише один місяць, @startOfPreviousMonthйого можна легко замінити / отримати:

DATEADD(month, -1, @startOCurrentfMonth)

Якщо вам потрібно вивести початок поточного місяця на сервері, ви можете зробити це за допомогою наступного:

DATEADD(month, DATEDIFF(month, 0, CURRENT_TIMESTAMP), 0)

Тут коротке пояснення. Початковий DATEDIFF(...)отримає різницю між початком поточної ери ( 0001-01-01- AD, CE, що завгодно), фактично повертаючи велике ціле число. Це кількість місяців до початку поточного місяця. Потім ми додаємо це число до початку ери, тобто на початку даного місяця.

Отже, ваш повний сценарій може / повинен виглядати приблизно так:

DECLARE @startOfCurrentMonth DATETIME
SET @startOfCurrentMonth = DATEADD(month, DATEDIFF(month, 0, CURRENT_TIMESTAMP), 0)

SELECT *
FROM Member
WHERE date_created >= DATEADD(month, -1, @startOfCurrentMonth) -- this was originally    misspelled
      AND date_created < @startOfCurrentMonth

Таким чином, всі операції з датою виконуються лише один раз, з одним значенням; оптимізатор може безкоштовно використовувати індекси, і жодні неправильні дані не включатимуться.


8
Я дуже хочу, навіть якщо це буде коштувати мені репутації, я можу проголосувати за це не раз. Молодці.
— Нокс,

2
Ти геній! Я ціную добре написану відповідь, яка не применшує, а лише інформує. Чудова робота! Я також погоджуюсь з @knox
— bird2920

Це повинна бути найкраща відповідь. Додаткова подяка за інформацію, що "МІЖ" не спрацювало б у цьому випадку, оскільки це застало б додатковий день.
— Косан

11

Додані параметри, які були надані до цього часу, взагалі не використовуватимуть ваші індекси.

Щось подібне зробить трюк і використає індекс у таблиці (якщо такий існує).

DECLARE @StartDate DATETIME, @EndDate DATETIME
SET @StartDate = dateadd(mm, -1, getdate())
SET @StartDate = dateadd(dd, datepart(dd, getdate())*-1, @StartDate)
SET @EndDate = dateadd(mm, 1, @StartDate)

SELECT *
FROM Member
WHERE date_created BETWEEN @StartDate AND @EndDate

Вибачте за голосування проти, але це не працює: спробуйте '12/31/2013 11:59' для контрприкладу (повертає рядки між '2013-10-30 11: 59: 00.000' та '2013-11-30 11: 59: 00.000 'на SQL Server).
— Даніель Коттер

1
getdate () дає вам поточний час. Я думаю, вам слід взяти час з 00:00 (початок дня). Відповідь @Rokas правильна. Якщо потрібно побачити, надрукуйте час початку та закінчення.
— madhu_karnati

10
DECLARE @StartDate DATETIME, @EndDate DATETIME
SET @StartDate = DATEADD(mm, DATEDIFF(mm,0,getdate())-1, 0)
SET @EndDate = DATEADD(mm, 1, @StartDate)

SELECT *
FROM Member
WHERE date_created BETWEEN @StartDate AND @EndDate

Оновлення рішення mrdenny, таким чином ви отримуєте саме минулий місяць від РРРР-ММ-01


3

Останній місяць вважати до останнього дня місяця. 31.01.2016 р. Тут останнім днем ​​місяця буде 31 січня. Що не схоже на останні 30 днів.

SELECT CONVERT(DATE, DATEADD(DAY,-DAY(GETDATE()),GETDATE()))

1

Один із способів зробити це за допомогою функції DATEPART :

select field1, field2, fieldN from TABLE where DATEPART(month, date_created) = 4 
and DATEPART(year, date_created) = 2009

поверне всі дати у квітні. Протягом минулого місяця (тобто попереднього до поточного місяця) ви також можете використовувати GETDATE та DATEADD :

select field1, field2, fieldN from TABLE where DATEPART(month, date_created) 
= (DATEPART(month, GETDATE()) - 1) and 
DATEPART(year, date_created) = DATEPART(year, DATEADD(m, -1, GETDATE()))

3
Цей підхід буде ігнорувати будь-які індекси в таблиці та виконувати сканування таблиці (або кластерного індексу) кожного разу, коли він запускається. Чим більша таблиця, тим довше триватиме запит.
— mrdenny

1
declare @PrevMonth as nvarchar(256)

SELECT @PrevMonth = DateName( month,DATEADD(mm, DATEDIFF(mm, 0, getdate()) - 1, 0)) + 
   '-' + substring(DateName( Year, getDate() ) ,3,4)

1

Запит SQL, щоб отримати запис лише поточного місяця

SELECT * FROM CUSTOMER
WHERE MONTH(DATE) = MONTH(CURRENT_TIMESTAMP) AND YEAR(DATE) = YEAR(CURRENT_TIMESTAMP);

0
select * from [member] where DatePart("m", date_created) = DatePart("m", DateAdd("m", -1, getdate())) AND DatePart("yyyy", date_created) = DatePart("yyyy", DateAdd("m", -1, getdate()))

2
Хоча цей код може відповісти на питання, надаючи додатковий контекст щодо того, чому та / або як цей код відповідає на питання, покращує його довгострокове значення.
— β.εηοιτ.βε

0
DECLARE @StartDate DATETIME, @EndDate DATETIME    
SET @StartDate = DATEADD(mm, DATEDIFF(mm, 0, getdate()) - 1, 0)    
SET @EndDate = dateadd(dd, -1, DATEADD(mm, 1, @StartDate))

SELECT * FROM Member WHERE date_created BETWEEN @StartDate AND @EndDate 

і ще одне оновлення до рішення mrdenny.
Він також вказує точний останній день попереднього місяця.


0
WHERE 
    date_created >= DATEADD(MONTH, DATEDIFF(MONTH, 31, CURRENT_TIMESTAMP), 0)
    AND date_created < DATEADD(MONTH, DATEDIFF(MONTH, 0, CURRENT_TIMESTAMP), 0)

Трохи пояснень?
— cheesemacfly

0

Я з Oracle env, і я б зробив це так в Oracle:

select * from table
where trunc(somedatefield, 'MONTH') =
trunc(sysdate -INTERVAL '0-1' YEAR TO MONTH, 'MONTH')

Ідея: я веду запланований звіт попереднього місяця (з 1-го дня до останнього дня місяця, без вікон). Це може бути несприятливим для індексування, але Oracle у будь-якому випадку швидко обробляє дату. Чи існує подібний простий і короткий спосіб у MS SQL? Відповідь порівняння року та місяця окремо здається дурним для людей Oracle.


0

За допомогою цього запиту ви можете отримати записи за останній місяць

SELECT * FROM dbo.member d 
WHERE  CONVERT(DATE, date_created,101)>=CONVERT(DATE,DATEADD(m, datediff(m, 0, current_timestamp)-1, 0)) 
and CONVERT(DATE, date_created,101) < CONVERT(DATE, DATEADD(m, datediff(m, 0, current_timestamp)-1, 0),101) 

0

Я не думаю, що прийняте рішення дуже зручне для індексів, я використовую натомість наступні рядки

select * from dbtable where the_date >= convert(varchar(10),DATEADD(m, -1, dateadd(d, - datepart(dd, GETDATE())+1, GETDATE())),120) and the_date <= dateadd(ms, -3, convert(varchar(10),DATEADD(m, 0, dateadd(d, - datepart(dd, GETDATE())+1, GETDATE())),120));

Або просто (це найкраще).

select * from dbtable where the_date >= convert(varchar(10),DATEADD(m, -1, dateadd(d, - datepart(dd, GETDATE())+1, GETDATE())),120) and the_date < SELECT convert(varchar(10),DATEADD(m, -1, dateadd(d, - datepart(dd, GETDATE())+1, GETDATE())),120);

Деякі допоможуть

-- Get the first of last month
SELECT convert(varchar(10),DATEADD(m, -1, dateadd(d, - datepart(dd, GETDATE())+1, GETDATE())),120);
-- Get the first of current month
SELECT convert(varchar(10),DATEADD(m, -1, dateadd(d, - datepart(dd, GETDATE())+1, GETDATE())),120);
--Get the last of last month except the last 3milli seconds. (3miliseconds being used as SQL express otherwise round it up to the full second (SERIUSLY MS)
SELECT dateadd(ms, -3, convert(varchar(10),DATEADD(m, 0, dateadd(d, - datepart(dd, GETDATE())+1, GETDATE())),120));

-1

На сервері Sql за останній місяць:

select * from tablename 
where order_date > DateAdd(WEEK, -1, GETDATE()+1) and order_date<=GETDATE()

-1
DECLARE @curDate INT = datepart( Month,GETDATE())
IF (@curDate = 1)
    BEGIN
        select * from Featured_Deal
        where datepart( Month,Created_Date)=12 AND datepart(Year,Created_Date) = (datepart(Year,GETDATE())-1)

    END
ELSE
    BEGIN
        select * from Featured_Deal
        where datepart( Month,Created_Date)=(datepart( Month,GETDATE())-1) AND datepart(Year,Created_Date) = datepart(Year,GETDATE())

    END 

-1
DECLARE @StartDate DATETIME, @EndDate DATETIME
SET @StartDate = dateadd(mm, -1, getdate())
SET @StartDate = dateadd(dd, datepart(dd, getdate())*-1, @StartDate)
SET @EndDate = dateadd(mm, 1, @StartDate)
set @StartDate = DATEADD(dd, 1 , @StartDate)

-1

Я вирішив подібну проблему шляхом додавання місяця до моєї частини SELECT

Month DATEADD(day,Created_Date,'1971/12/31') As Month

і тоді я додав заяву WHERE

Month DATEADD(day,Created_Date,'1971/12/31') = month(getdate())-1

-1

Якщо ви шукаєте минулий місяць, спробуйте це,

SELECT
FROM  #emp 
WHERE DATEDIFF(MONTH,CREATEDDATE,GETDATE()) = 1

Якщо ви шукаєте минулий місяць, спробуйте це,

SELECT
FROM #emp
WHERE DATEDIFF(day,CREATEDDATE,GETDATE()) between 1 and 30

1
Будь ласка, прочитайте попередні відповіді, щоб зрозуміти, чому це не спрацює, і \ чи не правильний спосіб зробити це, перш ніж люди проголосують проти. І, можливо, ви хочете видалити свою відповідь.
— Cetin Basoz

Привіт, ласкаво просимо до Stack Overflow. Відповідаючи на запитання, на яке вже є багато відповідей (17!), Не забудьте додати деяке додаткове розуміння того, чому поспішно складена відповідь є суттєвою, а не просто повторює те, що вже перевірено оригінальним плакатом. Це особливо важливо у відповідях типу "лише код", таких як ваша.
— chb


-1

Якщо ви шукаєте дані попереднього місяця:

date(date_created)>=date_sub(date_format(curdate(),"%Y-%m-01"),interval 1 month) and 
date(date_created)<=date_sub(date_format(curdate(),'%Y-%m-01'),interval 1 day)

Це також спрацює, коли рік зміниться. Це також буде працювати на MySQL.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.