Select from table given day - sql

So I have a table in SQL Server with a datetime column on it. I want to select all from this table:
select * from dbo.tblMessages
but I want to pass in a datetime parameter. Then I want to select all messages from the table that have the same day as the datetime column in tblMessages, not just ones posted in the past 24 hours, etc.
How would I do this?
Thanks.

This should use an index on MyDateTimeCol in tblMessages
select * from dbo.tblMessages
WHERE
MyDateTimeCol >= DATEADD(day, DATEDIFF(day, 0, #Mydatetimeparameter), 0)
AND
MyDateTimeCol < DATEADD(day, DATEDIFF(day, 0, #Mydatetimeparameter), 1)
Any function applied to MyDateTimeCol will prevent an index being used correctly, includin DATEDIFF between this and #Mydatetime

As you are on SQL Server 2008 you can just do
SELECT *
FROM tblMessages
WHERE CAST(message_date AS DATE) = CAST(#YourDateParameter AS DATE)
This is sargable. SQL Server will add a ComputeScalar to the plan that calls the internal GetRangeThroughConvert function and gets the start and end of the range to seek.

If you need to do this a lot, and if you're on SQL Server 2005 or newer, you could also do this:
add three computed columns for the day, month, year of your date and persist those
ALTER TABLE dbo.YourTable
ADD DayPortion AS DAY(YourDateTimeColumn) PERSISTED
-- do the same for MONTH(YourDateTimeColumn) and YEAR(YourDateTimeColumn)
put an index on the three columns:
CREATE NONCLUSTERED INDEX IX_DatePortions
ON dbo.tblMessages(YearPortion, MonthPortion, DayPortion)
now, you can search very easily and quickly for those days, months, year, and with the index, your search will be very performant and quick
SELECT (list of columns)
FROM dbo.tblMessages
WHERE YearPortion = 2011 AND MonthPortion = 4 AND DayPortion = 17
With this setup - three computed, persisted columns - you can now simply insert new rows into the table - those three columns will be calculated automatically.
Since they're persisted and indexed, you can easily and very efficiently search on those columns, too.
And with this flexibility, you can also easily find e.g. all rows for a given month or year very nicely.

Related

SQL Server : average count of alerts per day, not including days with no alerts

I have a table that acts as a message log, with the two key tables being TIMESTAMP and TEXT. I'm working on a query that grabs all alerts (from TEXT) for the past 30 days (based on TIMESTAMP) and gives a daily average for those alerts.
Here is the query so far:
--goback 30 days start at midnight
declare #olderdate as datetime
set #olderdate = DATEADD(Day, -30, DATEDIFF(Day, 0, GetDate()))
--today at 11:59pm
declare #today as datetime
set #today = dateadd(ms, -3, (dateadd(day, +1, convert(varchar, GETDATE(), 101))))
print #today
--Grab average alerts per day over 30 days
select
avg(x.Alerts * 1.0 / 30)
from
(select count(*) as Alerts
from MESSAGE_LOG
where text like 'The process%'
and text like '%has alerted%'
and TIMESTAMP between #olderdate and #today) X
However, I want to add something that checks whether there were any alerts for a day and, if there are no alerts for that day, doesn't include it in the average. For example, if there are 90 alerts for a month but they're all in one day, I wouldn't want the average to be 3 alerts per day since that's clearly misleading.
Is there a way I can incorporate this into my query? I've searched for other solutions to this but haven't been able to get any to work.
This isn't written for your query, as I don't have any DDL or sample data, thus I'm going to provide a very simple example instead of how you would do this.
USE Sandbox;
GO
CREATE TABLE dbo.AlertMessage (ID int IDENTITY(1,1),
AlertDate date);
INSERT INTO dbo.AlertMessage (AlertDate)
VALUES('20190101'),('20190101'),('20190105'),('20190110'),('20190115'),('20190115'),('20190115');
GO
--Use a CTE to count per day:
WITH Tots AS (
SELECT AlertDate,
COUNT(ID) AS Alerts
FROM dbo.AlertMessage
GROUP BY AlertDate)
--Now the average
SELECT AVG(Alerts*1.0) AS DayAverage
FROM Tots;
GO
--Clean up
DROP TABLE dbo.AlertMessage;
You're trying to compute a double-aggregate: The average of daily totals.
Without using a CTE, you can try this as well, which is generalized a bit more to work for multiple months.
--get a list of events per day
DECLARE #Event TABLE
(
ID INT NOT NULL IDENTITY(1, 1)
,DateLocalTz DATE NOT NULL--make sure to handle time zones
,YearLocalTz AS DATEPART(YEAR, DateLocalTz) PERSISTED
,MonthLocalTz AS DATEPART(MONTH, DateLocalTz) PERSISTED
)
/*
INSERT INTO #Event(EntryDateLocalTz)
SELECT DISTINCT CONVERT(DATE, TIMESTAMP)--presumed to be in your local time zone because you did not specify
FROM dbo.MESSAGE_LOG
WHERE UPPER([TEXT]) LIKE 'THE PROCESS%' AND UPPER([TEXT]) LIKE '%HAS ALERTED%'--case insenitive
*/
INSERT INTO #Event(DateLocalTz)
VALUES ('2018-12-31'), ('2019-01-01'), ('2019-01-01'), ('2019-01-01'), ('2019-01-12'), ('2019-01-13')
--get average number of alerts per alerting day each month
-- (this will not return months with no alerts,
-- use a LEFT OUTER JOIN against a month list table if you need to include uneventful months)
SELECT
YearLocalTz
,MonthLocalTz
,AvgAlertsOfAlertingDays = AVG(CONVERT(REAL, NumDailyAlerts))
FROM
(
SELECT
YearLocalTz
,MonthLocalTz
,DateLocalTz
,NumDailyAlerts = COUNT(*)
FROM #Event
GROUP BY YearLocalTz, MonthLocalTz, DateLocalTz
) AS X
GROUP BY YearLocalTz, MonthLocalTz
ORDER BY YearLocalTz ASC, MonthLocalTz ASC
Some things to note in my code:
I use PERSISTED columns to get the month and year date parts (because I'm lazy when populating tables)
Use explicit CONVERT to escape integer math that rounds down decimals. Multiplying by 1.0 is a less-readable hack.
Use CONVERT(DATE, ...) to round down to midnight instead of converting back and forth between strings
Do case-insensitive string searching by making everything uppercase (or lowercase, your preference)
Don't subtract 3 milliseconds to get the very last moment before midnight. Change your semantics to interpret the end of a time range as exclusive, instead of dealing with the precision of your datatypes. The only difference is using explicit comparators (i.e. use < instead of <=). Also, DATETIME resolution is 1/300th of a second, not 3 milliseconds.
Avoid using built-in keywords as column names (i.e. "TEXT"). If you do, wrap them in square brackets to avoid ambiguity.
Instead of dividing by 30 to get the average, divide by the count of distinct days in your results.
select
avg(x.Alerts * 1.0 / x.dd)
from
(select count(*) as Alerts, count(distinct CAST([TIMESTAMP] AS date)) AS dd
...

Update all date columns in SQL Server -1 day

I want to update my database (SQL Server Express) all the dates for specific ids.
I am displaying the ids I want to.
SELECT TOP (1000) ID, Dates
FROM tbl_table
WHERE (id IN (29695, 29700, 29701, 29702, 29703, 29704, 29705, 29706, 29707, 29708, 29709, 29710, 29711, 29712, 29713, 29714, 29715))
AND my dates in the database are like this:
Is there any way to update all the date columns with same date - 1 day?
For example: if we have 2019-12-20, update it to 2019-12-19?
For example if I want to do it in PHP, I would loop through this query and fetch all all dates. After I would remove one day like this:
date('m/d/Y', strtotime($date. ' - 1 days');
And create a query to update all the columns based on id. I just want to avoid that. Is there any SQL command that do that?
Thanks
The request below will update the rows you want by adding -1 days on each date:
UPDATE tbl_table
SET dates = Dateadd(day, -1, dates)
WHERE id IN ( 29695, 29700, 29701, 29702,
29703, 29704, 29705, 29706,
29707, 29708, 29709, 29710,
29711, 29712, 29713, 29714, 29715 )
DATEADD function takes 3 parameters:
the interval ( day, month, year ..)
the increment (the value to add or remove if negative)
an expression (wich is a datetime type)
See DATEADD documentation
To return a query with the previous day:
SELECT TOP (1000) ID, Dates, DATEADD(dd, -1, Dates) AS PreviousDay
FROM tbl_table
To update the table with the previous day:
UPDATE tbl_table
SET Dates = DATEADD(dd, -1, Dates)
FROM -- Put your conditions here
UPDATE tableName SET date= DATEADD(d,-1, date)
where ....
( here you put where clause for you required)

SQL find period that contain dates of specific year

I have a table (lets call it AAA) containing 3 colums ID,DateFrom,DateTo
I want to write a query to return all the records that contain (even 1 day) within the period DateFrom-DateTo of a specific year (eg 2016).
I am using SQL Server 2005
Thank you
Another way is this:
SELECT <columns list>
FROM AAA
WHERE DateFrom <= '2016-12-31' AND DateTo >= '2016-01-01'
If you have an index on DateFrom and DateTo, this query allows Sql-Server to use that index, unlike the query in Max xaM's answer.
On a small table you will probably see no difference but on a large one there can be a big performance hit using that query, since Sql-Server can't use an index if the column in the where clause is inside a function
Try this:
SELECT * FROM AAA
WHERE DATEPART(YEAR,DateFrom)=2016 OR DATEPART(YEAR,DateTo)=2016
Well you can use the following query
select * from Table1
WHERE DateDiff(day,DateFrom,DateTo)>0
AND YEAR(DateFrom) = YEAR(DateTo)
And here is the result:
Enjoy :D !

Joining multiple tables returning duplicates

I am trying the following select statement including columns from 4 tables. But the results return each row 4 times, im sure this is because i have multiple left joins but i have tried other joins and cannot get the desired result.
select table1.empid,table2.name,table2.datefrom, table2.UserDefNumber1, table3.UserDefNumber1, table4.UserDefChar6
from table1
inner join table2
on table2.empid=table1.empid
inner join table3
on table3.empid=table1.empid
inner join table4
on table4.empid=table1.empid
where MONTH(table2.datefrom) = Month (Getdate())
I need this to return the data without any duplicates so only 1 row for each entry.
I would also like the "where Month" clause at the end look at the previous month not the current month but struggling with that also.
I am a bit new to this so i hope it makes sense.
Thanks
If the duplicate rows are identical on each column you can use the DISTINCT keyword to eliminate those duplicates.
But I think you should reconsider your JOIN or WHERE clause, because there has to be a reason for those duplicates:
The WHERE clause hits several rows in table2 having the same month on a single empid
There are several rows with the same empid in one of the other tables
both of the above is true
You may want to rule those duplicate rows out by conditions in WHERE/JOIN instead of the DISTINCT keyword as there may be unexpected behaviour when some data is changing in a single row of the original resultset. Then you start having duplicate empids again.
You can check if a date is in the previous month by following clause:
date BETWEEN dateadd(mm, -1, datefromparts(year(getdate()), month(getdate()), 1))
AND datefromparts(year(getdate()), month(getdate()), 1)
This statment uses DATEFROMPARTS to create the beginning of the current month twice, subtract a month from the first one by using DATEADD (results in the beginning of the previous month) and checks if date is between those dates using BETWEEN.
If your query is returning duplicates, then one or more of the tables have duplicate empid values. This is a data problem. You can find them with queries like this:
select empid, count(*)
from table1
group by empid
having count(*) > 1;
You should really fix the data and query so it returns what you want. You can do a bandage solution with select distinct, but I would not usually recommend that. Something is causing the duplicates, and if you do not understand why, then the query may not be returning the results you expect.
As for your where clause. Given your logic, the proper way to express this would include the year:
where year(table2.datefrom) = year(getdate()) and
month(table2.datefrom) = month(Getdate())
Although there are other ways to express this logic that are more compatible with indexes, you can continue down this course with:
where year(table2.datefrom) * 12 + month(table2.datefrom) = year(getdate()) * 12 + Month(Getdate()) - 1
That is, convert the months to a number of months since time zero and then use month arithmetic.
If you care about indexes, then your current where clause would look like:
where table2.datefrom >= dateadd(day,
- (day(getdate) - 1),
cast(getdate() as date) and
table2.datefrom < dateadd(day,
- (dateadd(month, 1, getdate()) - 1),
cast(dateadd(month, 1, getdate()) as date)
Eliminate duplicates from your query by including the distinct keyword immediately after select
Comparing against a previous month is slightly more complicated. It depends what you mean:
If the report was run on the 23rd Jan 2015, would you want 01/12/2014-31/12/2014 or 23/12/2014-22/01/2015?

How to rearrange the value of column

I have a table (tblDates). In this table I have two column (Date,Age) . Now I want If I add new date in this table then Age column rearranged there values.
Table - tblDates
Date Age
--------------------
12/01/14 5
12/02/14 4
12/03/14 3
12/04/14 2
12/05/14 1
If I add New date i.e., 12/06/14 then I want result like this
Table - tblDates
Date Age
--------------------
12/01/14 6
12/02/14 5
12/03/14 4
12/04/14 3
12/05/14 2
12/06/14 1
I may be reading too much into your question, but if your goal is to compute the age (in days) from a given date (today?) to the date stored in your tables, then you'll be better off using the DATEDIFF function and computing the value when you query it each time.
For example:
-- Option 1: Compute when you query it each time in the query you require it
SELECT d.[Date], DATEDIFF(dd, d.[Date], CONVERT(DATE, GETDATE())) as [Age]
FROM tblDates AS d
You can also define the Age column on your table as a Computed Column if it will be used frequently enough, or wrap the table in a View to embed this computation:
-- Option 2: Compute at query time, but build the computation into the table definition
CREATE TABLE [dbo].[tblDates] (
[Date] DATE NOT NULL,
[AgeInDaysComputed] AS (DATEDIFF(dd, [Date], CONVERT(DATE, GETDATE())) )
)
GO
-- Option 3: Compute at query time, but require caller interact with a different object
-- (view) to get the computation
CREATE VIEW [dbo].[vwDates]
AS
SELECT d.[Date], DATEDIFF(dd, d.[Date], CONVERT(DATE, GETDATE())) as [AgeInDays]
FROM dbo.tblDates AS D
GO
One note regarding the GETDATE function: you need to be aware of your server timezone, as GETDATE returns the date according to your server's local timezone. As long as your server configuration and user's configurations are all in the same timezone, this should provide the correct result.
(If the age in days is what you're trying to compute, you may want to edit your question to better reflect this intent for the benefit of future readers, as it is quite different from "rearranging the value of columns")
Pull the values that you want out when you query, not when you insert data. You seem to want:
select d.*, row_number() over (order by date desc) as age
from tblDates d;
Otherwise, your insert operation will become very cumbersome, requiring changes to all the rows in the table.