Отримайте записи минулого місяця на сервері 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.