I am facing issues with my query:
select week_number
,year
from accounting_calender
where week_number<=3
and week_number>3-6
and year=2013
Here in this query I'm passing the week_number 3 and year 2013 through my reporting tool.
I'm getting the following output:
| Week_number | year |
----------------------
| 3 | 2013 |
| 2 | 2013 |
| 1 | 2013 |
But here in my accounting calender table I returned entries for 2012 too.
So here I'm subtracting -6, so it has to go to previous year weeks also.
I am looking for something like below output:
| Week_number | year |
----------------------
| 51 | 2012 |
| 52 | 2012 |
| 53 | 2012 |
| 3 | 2013 |
| 2 | 2013 |
| 1 | 2013 |
I have read-only access.
You'll need to add a special case where the previous 6 weeks cross a year boundary:
select
week_number,
year
from
accounting_calender
where
(week_number > #week-6 and week_number <= #week and year=#year)
or
(week_number > #week-6+53 and year=#year-1)
If #week >= 6, then the second condition will always be > 53 so it will have no effect. However, if #week < 6, then the second condition will be 52, 51, etc. for the previous year.
Converting your weeks and years into dates will make it much easier to perform additions to your date range:
DECLARE #Week_Number INT
DECLARE #Year INT
DECLARE #WeeksToGet INT
SET #Week_Number = 3
SET #Year = 2013
SET #WeeksToGet = 6
DECLARE #StartDate DATETIME
DECLARE #EndDate DATETIME
SET #EndDate = DATEADD(WEEK, #Week_Number, DATEADD(YEAR, #Year - 1900, 0))
SET #StartDate= DATEADD(WEEK, -6, #EndDate)
select
week_number,
year
from accounting_calender
where
DATEADD(WEEK, week_number, DATEADD(YEAR, year - 1900, 0)) between
#StartDate AND #EndDate
However, note that this will obfuscate any indexes that you have on week_number and year. If this is a concern, you might consider changing these columns into a DATE type so that you can avoid having to convert the two columns into one date.
Sql Fiddle
Related
In MySQL database, I have table with YEAR, MONTH, DAY columns.
| YEAR | MONTH | DAY | VALUE |
|----------------------------|
| 2018 | 11 | 9 | 1056 |
| 2018 | 11 | 10 | 6582 |
|****************************|
| 2018 | 12 | 9 | 6892 |
| 2018 | 12 | 10 | 5987 |
|****************************|
| 2019 | 3 | 5 | 5693 |
| 2019 | 3 | 6 | 5698 |
I need to take all values from the table between 2 date.
Let's say between 2018-11-09 and 2019-03-05 or between 2018-11-10 and 2018-12-09.
I need to say that unfortunately I can't merge these three column for one datetime column. Also the table has partitioning by that 3 columns.
Datatype of columns: smallint(6)
It seems this query should give you the results you want. It creates a date string out of your 3 columns and then uses STR_TO_DATE to convert that into a value that MySQL can compare with the search date strings.
SELECT *
FROM table1
WHERE STR_TO_DATE(CONCAT_WS('/', `DAY`, `MONTH`, `YEAR`), '%d/%m/%Y') BETWEEN '2018-11-09' AND '2019-03-05'
Output
YEAR MONTH DAY VALUE
2018 11 9 1056
2018 11 10 6582
2018 12 9 6892
2018 12 10 5987
2019 3 5 5693
Demo on dbfiddle
One way is to create a string in MySQL Date format (YYYY-MM-DD) using string functions such as Concat() and Lpad():
SELECT *
FROM your_table
WHERE CONCAT(`YEAR`, '-', LPAD(`MONTH`,2,'0'), '-', LPAD(`DAY`,2,'0'))
BETWEEN '2018-11-09' AND '2019-03-05'
Based on further discussion in comments, if you can input the year, month, and day value separately for the given data range(s); instead of creating a date using functions, we can directly use the respective columns instead. This will also allow us to utilize indexes (if defined) on these columns.
SELECT *
FROM your_table
WHERE
/* Condition for the month in the start date of the range */
(YEAR = 2018 AND MONTH = 11 AND DAY >= 9)
OR
/* Condition for the rest of the months in start date year */
(YEAR = 2018 AND MONTH > 11)
OR
/* Condition for the month in the end date of the range */
(YEAR = 2019 AND MONTH = 3 AND DAY <= 5)
OR
/* Condition for the rest of the months in end date year */
(YEAR = 2019 AND MONTH < 3)
OR
/* Condition for the years between the start and end date */
(YEAR > 2018 AND YEAR < 2019)
Above mentioned conditions can be compressed further. But I have written in this manner, for ease of understand-ability.
However, it is recommended to create another column to store the date in Date format. If you cannot make changes to the application code, and if your MySQL version >= 5.7, you can look at Generated Columns, and refer to that in your SELECT query instead.
ALTER TABLE your_table
ADD COLUMN date_col DATE
GENERATED ALWAYS AS CONCAT(`YEAR`, '-', LPAD(`MONTH`,2,'0'), '-', LPAD(`DAY`,2,'0')) STORED;
Then, the SELECT query becomes trivial:
SELECT * FROM your_table
WHERE date_col BETWEEN '2018-11-09' AND '2019-03-05'
Hi I have reports that have been issued with some regularity but they have an initial value of accounts for when the report value is null.
I would like to create a new variable Accts_N which occurs for School, Year, and date and is the sum of that date's Accts value and the Accts value when the date is null.
So, using the sample table below, School A Year 2017 would have a Accts_N value of 8 for 2016-01-10 and a value of 12 for 2016-02-10.
School | Year | Accts | ReportDate
-------|------|-------|-----------
A | 2017 | 2 | null
A | 2017 | 6 | 2016-01-10
A | 2017 | 10 | 2016-02-10
A | 2018 | 0 | 2016-01-10
A | 2018 | 4 | 2016-02-10
B | 2017 | 9 | null
B | 2018 | 3 | 2016-2-10
I've tried a few different instances of SUM CASE WHEN but I don't think that's the right approach. Can someone suggest a direction for me?
Thank you
If you want to add a new column, then a correlated subquery comes to mind:
select r.*,
(select sum(r2.accts)
from reports r2
where r2.school = r.school and
r2.year = r.year and
(r2.reportdate = r.reportdate or r2.reportdate is null)
) as accts_n
from reports r;
How about this?
declare #t table(School nvarchar(50), Year datetime, Accts int, ReportDate datetime)
insert into #t
values
('A','2017',2,null),
('A','2017',6,'2016-01-10'),
('A','2017',10,'2016-02-10'),
('A','2018',0,'2016-01-10'),
('A','2018',4,'2016-02-10'),
('B','2017',9,null),
('B','2018',3,'2016-01-10')
select t.School, t.Year, t.ReportDate, t.Accts + ISNULL(tNulls.SumAcctsWhenNull,0)
from #t t
outer apply (select t2.School, t2.Year, SUM(Accts) AS SumAcctsWhenNull
from #t t2
where
t2.ReportDate IS NULL AND
t2.School = t.School AND
t2.Year = t.Year
group by t2.School, t2.Year) tNulls
where
t.ReportDate IS NOT NULL
I have this table: table1, and four varaible $START_MONTH $START_YEAR $END_MONTH $END_YEAR which return: month:1, year: 2014 and 3, 2015
+----+-----+-------+------+-------------+
| ID | DAY | MONTH | YEAR | DESCRIPTION |
+----+-----+-------+------+-------------+
| 1 | 1 | 1 | 2014 | Product 1 |
| 2 | 1 | 2 | 2015 | Product 2 |
| 3 | 2 | 3 | 2014 | Product 1 |
| 4 | 10 | 1 | 2015 | Product 3 |
| 5 | 9 | 5 | 2015 | Product 3 |
+----+-----+-------+------+-------------+
SELECT
DATE_FORMAT (CONCAT (DAY,' ', MONTH,' ',YAR), GET_FORMAT(DATE,'EUR')) AS NEW_DATA,
DESCRIPTION
FROM table1
WHERE
NEW_DATA BETWEEN $START_MONTH . $START_YEAR AND $END_MONTH . $END_YEAR
I need return the rows included between: 1.2014 and 3.2015. Thank you!
In MySQL you can filter by combined columns:
SELECT *
FROM table1
WHERE (`YEAR`, `MONTH`) >= (2014,1)
AND (`YEAR`, `MONTH`) <= (2015,3)
http://sqlfiddle.com/#!9/87000/2
Update: As Strawberry stated, MySQL will not use an index for this condition. But it's not that it's impossible - MySQL is just missing this optimisation. As far as i know PostgreSQL is fine with this kind of index range check. So we can hope that MySQL/MariaDB will support it in the future.
However, if you face performance problems, you can add redundant conditions for the year range:
WHERE (`YEAR`, `MONTH`) >= (2014,1)
AND (`YEAR`, `MONTH`) <= (2015,3)
AND `YEAR` >= 2014
AND `YEAR` <= 2015
This way MySQL will first filter the rows by YEAR using an index like YEAR, MONTH or YEAR, MONTH, DAY (or any index that starts with YEAR).
All alternatives i know are not very readable and no of them can use an index:
WHERE DATE(CONCAT_WS('-', `YEAR`, `MONTH`, 1)) >= '2014-01-01'
AND DATE(CONCAT_WS('-', `YEAR`, `MONTH`, 1)) <= '2015-03-01'
Or
WHERE (`YEAR` = 2014 AND `MONTH` >= 1)
OR (`YEAR` = 2015 AND `MONTH` >= 3)
OR (`YEAR` > 2014 AND `YEAR` < 2015)
If you are fighting for every milisecond you can also create an indexed generated (virtual) column (MySQL 5.7):
`DATE` AS STR_TO_DATE(CONCAT_WS('-', `YEAR`, `MONTH`, `DAY`),'%Y-%m-%d') VIRTUAL KEY
And query like:
WHERE `DATE` >= '2014-01-01'
AND `DATE` < '2015-04-01'
SELECT
DATE_FORMAT (CONCAT (DAY,' ', MONTH,' ',YEAR), GET_FORMAT(DATE,'EUR')) AS NEW_DATA,
DESCRIPTION
FROM table1
WHERE
(YEAR BETWEEN $START_YEAR AND $END_YEAR)
AND (MONTH BETWEEN $START_MONTH AND $END_MONTH)
I have the below table, pretty simple.
==========================================================================
attendanceID | agentID | incurredDate | points | comment
==========================================================================
10 | vimunson | 2013-07-22 | 2 | Some Text
11 | vimunson | 2013-07-29 | 2 | Some Text
12 | vimunson | 2013-12-06 | 1 | Some Text
The his query below:
SELECT
attendanceID,
agentID,
incurredDate,
leadDate,
points,
#1F:=IF(incurredDate <= curdate() - 90
AND leadDate = NULL,
points - 1,
IF(DATEDIFF(leadDate, incurredDate) > 90,
points - 1,
points)) AS '1stFallOff',
#2F:=IF(incurredDate <= curdate() - 180
AND leadDate = NULL,
points - 2,
IF(DATEDIFF(leadDate, incurredDate) > 180,
points - 2,
#1F)) AS '2ndFallOff',
IF(#total < 0, 0, #total:=#total + #2F) AS Total,
comment,
linked
FROM
(SELECT
attendanceID,
mo.agentID,
#r AS leadDate,
(#r:=incurredDate) AS incurredDate,
comment,
points,
linked
FROM
(SELECT
m . *
FROM
(SELECT #_date = NULL, #total:=0) varaible, attendance m
ORDER by agentID , incurredDate desc) mo
where
agentID = 'vimunson'
AND (case
WHEN #_date is NULL or #_date <> incurredDate THEN #r:=NULL
ELSE NULL
END IS NULL)
AND (#_date:=incurredDate) IS NOT NULL) T
ORDER BY agentID , incurredDate
When I run the query it returns the below:
========================================================================================================================================
attendanceID | agentID | incurredDate | leadDate | points | 1stFallOff | 2ndFallOff | Total | comment
========================================================================================================================================
10 | vimunson | 2013-07-22 | NULL | 2 | 2 | 2 | 2 | Some Text
11 | vimunson | 2013-07-29 | NULL | 2 | 2 | 2 | 4 | Some Text
12 | vimunson | 2013-12-06 | NULL | 1 | 2 | 1 | 5 | Some Text
I cannot figure out why the leadDate column is `null'. I have narrowed it down to a user session. For example if I run it again with the same user session it will come back with what I want.
The way variables #r and #_date are passed around relies on a specific order in which certain parts of the query are evaluated. That's a risky assumption to make in a query language that is declarative rather than imperative. The more sophisticated a query optimizer is, the more unpredictable the behaviour of this query will be. A 'simple' engine might follow your intentions, another engine might adapt its behaviour as you go, for example because it uses temporary indexes to improve query performance.
In situations where you need to pass values from one row to another, it would be better to use a cursor.
http://dev.mysql.com/doc/refman/5.0/en/cursors.html
EDIT: sample code below.
I focused on column 'leadDate'; implementation of the falloff and total columns should be similar.
CREATE PROCEDURE MyProc()
BEGIN
DECLARE done int DEFAULT FALSE;
DECLARE currentAttendanceID int;
DECLARE currentAgentID, previousAgentID varchar(8);
DECLARE currentIncurredDate date;
DECLARE currentLeadDate date;
DECLARE currentPoints int;
DECLARE currentComment varchar(9);
DECLARE myCursor CURSOR FOR
SELECT attendanceID, agentID, incurredDate, points, comment
FROM attendance
ORDER BY agentID, incurredDate DESC;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
CREATE TEMPORARY TABLE myTemp (
attendanceID int,
agentID varchar(8),
incurredDate date,
leadDate date,
points int,
comment varchar(9)
) ENGINE=MEMORY;
OPEN myCursor;
SET previousAgentID := NULL;
read_loop: LOOP
FETCH myCursor INTO currentAttendanceID, currentAgentID, currentIncurredDate, currentPoints, currentComment;
IF done THEN
LEAVE read_loop;
END IF;
IF previousAgentID IS NULL OR previousAgentID <> currentAgentID THEN
SET currentLeadDate := NULL;
SET previousAgentID := currentAgentID;
END IF;
INSERT INTO myTemp VALUES (currentAttendanceID, currentAgentID, currentIncurredDate, currentLeadDate, currentPoints, currentComment);
SET currentLeadDate := currentIncurredDate;
END LOOP;
CLOSE myCursor;
SELECT *
FROM myTemp
ORDER BY agentID, incurredDate;
DROP TABLE myTemp;
END
FYC: http://sqlfiddle.com/#!2/910a3/1/0
Try this:
SELECT a.attendanceID, a.agentID, a.incurredDate, a.leadDate, a.points,
#1F:= (CASE WHEN incurredDate <= CURDATE()-90 AND leadDate=NULL THEN points-1
WHEN DATEDIFF(leadDate, incurredDate) > 90 THEN points-1
ELSE points
END) AS '1stFallOff',
#2F:= (CASE WHEN incurredDate <= CURDATE()-180 AND leadDate=NULL THEN points-2
WHEN DATEDIFF(leadDate, incurredDate) > 180 THEN points-2
ELSE #1F
END) AS '2ndFallOff',
(#Total:=#Total + a.points) AS Total, a.comment
FROM (SELECT a.attendanceID, a.agentID, a.incurredDate, b.incurredDate leadDate, a.points, a.comment
FROM attendance a
LEFT JOIN attendance b ON a.agentID = b.agentID AND a.incurredDate < b.incurredDate
GROUP BY a.agentID, a.incurredDate
) AS A, (SELECT #Total:=0, #1F:=0, #2F:=0) B;
Check the SQL FIDDLE DEMO
OUTPUT
| ATTENDANCEID | AGENTID | INCURREDDATE | LEADDATE | POINTS | 1STFALLOFF | 2NDFALLOFF | TOTAL | COMMENT |
|--------------|----------|---------------------------------|---------------------------------|--------|------------|------------|-------|-----------|
| 10 | vimunson | July, 22 2013 00:00:00+0000 | July, 29 2013 00:00:00+0000 | 2 | 2 | 2 | 2 | Some Text |
| 11 | vimunson | July, 29 2013 00:00:00+0000 | December, 06 2013 00:00:00+0000 | 2 | 1 | 1 | 4 | Some Text |
| 12 | vimunson | December, 06 2013 00:00:00+0000 | (null) | 1 | 1 | 1 | 5 | Some Text |
After reviewing multiple results I was able to come up with something that is what I expect. I have entered more data and the below syntax. I liked the idea of the cursor but it was not ideal for my use, so I did not use it. I did not want to use CASE or any JOINS since they can be complex.
http://sqlfiddle.com/#!2/2fb86/1
SELECT
attendanceID,
agentID,
incurredDate,
#ld:=(select
incurredDate
from
attendance
where
incurredDate > a.incurredDate
and agentID = a.agentID
order by incurredDate
limit 1) leadDate,
points,
#1F:=IF(incurredDate <= DATE_SUB(curdate(),
INTERVAL IF(incurredDate < '2013-12-02', 90, 60) DAY)
AND #ld <=> NULL,
points - 1,
IF(DATEDIFF(COALESCE(#ld, '1900-01-01'),
incurredDate) > IF(incurredDate < '2013-12-02', 90, 60),
points - 1,
points)) AS '1stFallOff',
#2F:=IF(incurredDate <= DATE_SUB(curdate(),
INTERVAL IF(incurredDate < '2013-12-02',
180,
120) DAY)
AND getLeadDate(incurredDate, agentID) <=> NULL,
points - 1,
IF(DATEDIFF(COALESCE(#ld, '1900-01-01'),
incurredDate) > IF(incurredDate < '2013-12-02',
180,
120),
points - 2,
#1F)) AS '2ndFallOff',
IF((#total + #2F) < 0,
0,
IF(DATE_ADD(incurredDate, INTERVAL 365 DAY) <= CURDATE(),
#total:=0,
#total:=#total + #2F)) AS Total,
comment,
linked,
DATE_ADD(incurredDate, INTERVAL 365 DAY) AS 'fallOffDate'
FROM
(SELECT #total:=0) v,
attendance a
WHERE
agentID = 'vimunson'
GROUP BY agentID , incurredDate
How should I convert number to date?
for example:-
I would like to enter number as 31, it should find which all months has date as 31 and it should show out put as below for current year.
Date Day
31-01-2013 Thursday
31-03-2013 Sunday
And how should I convert number to date.
I'm not sure if this is exactly what you want to do, but if you create a calendar table first then it's a very simple query:
select [Date], [Day]
from dbo.Calendar
where YearNumber = 2013 and DayNumber = 31
You could use the day() function to compare the day in your date column to the value that you provide. Then you can use datename() to get the weekday:
declare #val int = 31
select dt, datename(dw, dt)
from yourtable
where day(dt) = #val
See SQL Fiddle with Demo
SQL Fiddle
MS SQL Server 2008 Schema Setup:
Query 1:
declare #Day int;
set #Day = 31
select D.D,
datename(weekday, D.D) as DOW
from (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) as M(M)
cross apply (select dateadd(day,
#Day - 1,
dateadd(month,
M.M - 1,
dateadd(year,
year(getdate()) - 1,
cast('00010101' as date))))) as D(D)
where day(D.D) = #Day
Results:
| D | DOW |
--------------------------
| 2013-01-31 | Thursday |
| 2013-03-31 | Sunday |
| 2013-05-31 | Friday |
| 2013-07-31 | Wednesday |
| 2013-08-31 | Saturday |
| 2013-10-31 | Thursday |
| 2013-12-31 | Tuesday |