SQL search between two days without datetime - mysql

Trying to search in a database a date range. Problem is, I cannot use the datetime column type in my database. To compensate, date's are displayed as three columns. a Month column, a Day column, and a Year column. Here is my SQL query:
SELECT COUNT(*)
FROM `import`
WHERE `call_day` BETWEEN 29 AND 15
AND `call_month` BETWEEN 9 AND 10
AND `call_year` BETWEEN 2013 AND 2013
You can see where I run into trouble. call_day needs to search between the 29th day and the 15th day. This won't work because 15 is smaller than 29, but I need it to work because the month is in the future :)
Any thoughts/solutions? No I cannot change the database in any way. Read only.

Concat the values like yyyymmdd and then you can compare them like strings.

Besides the concatenation approach, which can be implemented in quite a few ways, e.g.
SELECT *
FROM import
WHERE STR_TO_DATE(CONCAT_WS('-', call_year, call_month, call_day), '%Y-%c-%e')
BETWEEN '2013-09-29' AND '2013-10-15'
or
SELECT *
FROM import
WHERE CONCAT(call_year, LPAD(call_month, 2, '0'), LPAD(call_day, 2, '0'))
BETWEEN '20130929' AND '20131015'
Here is SQLFiddle demo
that will always cause a full scan and assuming that date ranges in your queries usually don't span more than a few months you can also do
SELECT *
FROM import
WHERE (call_year = 2013 AND
call_month = 9 AND
call_day BETWEEN 29 AND DAY(LAST_DAY('2013-09-01'))) -- or just 30
OR (call_year = 2013 AND
call_month = 10 AND
call_day BETWEEN 1 AND 15)
Here is SQLFiddle demo
For a query that spans a year (e.g. from 2012-08-20 to 2013-10-15)
SELECT *
FROM import
WHERE (call_year = 2012 AND
call_month = 8 AND
call_day BETWEEN 20 AND 31)
OR (call_year = 2012 AND
call_month BETWEEN 9 AND 12 AND
call_day BETWEEN 1 AND 31)
OR (call_year = 2013 AND
call_month BETWEEN 1 AND 9 AND
call_day BETWEEN 1 AND 31)
OR (call_year = 2013 AND
call_month = 10 AND
call_day BETWEEN 1 AND 15)
Here is SQLFiddle demo

As PeterM said, this may play hell with performance, but if you're storing these as integers, you can do the following:
(year * 10000) + (month* 100) + day will always yield an 8 byte "date".
2013 *10000 = 20130000
9 * 100 = 900
15
20130915
It's an ugly hack, and it will be expensive, because you'll be evaluating every row in your table, if you can't limit the rows in any other way, but I think it'll work.
EDIT:
Typing is hard!

SELECT COUNT(*) FROM `import`
WHERE CONCAT(CAST(`call_year`AS CHAR(4)) , RIGHT(CONCAT('00',CAST(`call_month`AS CHAR(2))),2) , RIGHT(CONCAT('00',CAST(`call_day`AS CHAR(2))),2))
BETWEEN '20130929' AND '20131015'

Related

SSRS - calculate percent change with variable years and number of years

Hopefully, I can explain this clearly! Our report allows user to select a variable number of academic years. They can be consecutive or not. In this case, say the user selects 2017, 2015, 2013. The report presents like this:
AcYear TotCredits %Change
2013 251 0.00
2015 255 0.00
2017 1102 0.00
Because the SQL that provides the datasource is assuming the previous year is one year prior. So 2015 is being compared against 2014, even though we're not selecting that info, which is why it's 0. 2017 is being compared against 2016, even though the user selected 2015. So, either in the initial SQL or in the report table expression, how would I go about getting the credits for the most immediately prior SELECTED academic year, no matter how many were selected, and then calculating the percent change based on that??
(If the user selected consecutive years, say 2017 and 2016, the data looks like this:)
AcYear TotCredits %Change
2016 458 0.00%
2017 19 -95.85%
This is the self join to get the "previous" year:
from
cte
left join cte as prev1
on cte.academic_year - 1 = prev1.academic_year
and cte.subject = prev1.subject
Thanks for any ideas!
You could try to use previous function to see whether it works or not. You need to make sure Year has been sorted. Below is design
select * from (select 2012 as year, 12 as amount
union all
select 2013 as year, 5 as amount
union all
select 2014 as year, 6 as amount
union all
select 2015 as year, 4 as amount
union all
select 2016 as year, 24 as amount)tt
where year in (#t)
Expression:
=iif(Fields!year.Value-previous(Fields!year.Value)=1,(Fields!amount.Value-previous(Fields!amount.Value))/iif(IsNothing(previous(Fields!amount.Value) ), 0,previous(Fields!amount.Value)),0)
Then you will get the result like below
Zoe
Surprisingly easy/elegant fix, just took me a long time to find it. The original import bit:
SELECT
cte.academic_year
, cte.subject
, cte.subject_desc
, cte.credits as cur_credits
, prev.credits as prev_credits
FROM
cte
LEFT JOIN cte as prev
on cte.academic_year - 1 = prev.academic_year
and cte.subject = prev.subject
The new improved code:
SELECT
t.academic_year
, t.subject
, t.subject_desc
, t.credits as cur_credits
, prev_credits = prev.credits
FROM
cte t
OUTER APPLY
(SELECT top 1 credits from
cte
WHERE t.academic_year > academic_year
and t.subject = subject
ORDER BY academic_year desc) prev
It would have been very nice to use LAG, but alas, we are on SQL2008.

How do I match a month range in the database to two dates?

Assume I have a table that contains a from_month (int, 1-12) and a to_month (int, 1-12) column, and I then have another table that contains a from (int, unix timestamp) and to (int, unix timestamp) column.
How would I match rows where the from_month and to_month columns contain my from and to timestamps, taking into account year boundaries on either side?
Scenario:
With from_month as 11 and to_month as 2, is there a way where I can get a from value of 1383264000 and a to value of 1391212800 to match?
At the same time however:
With from_month as 3 and to_month 8, is there a way where I can get a from value of 1362096000 and a to value of 1375315200 to match?
Clarifications:
I need records from the second table where the months from both timestamps fall between the range set out from the first table.
Also, I can obtain the month from the timestamp, so it's not necessary for MySQL to parse it, but the solution is likely unaffected.
Sorry but i'm not sure how you want to compare with the year
But if only month, you can use FROM_UNIXTIME to get date from a unixtime, and MONTH to get month from a date
for example
SELECT FROM_UNIXTIME(1391212800)
> February, 01 2014 00:00:00+0000
SELECT MONTH(FROM_UNIXTIME(1391212800))
> 2
with the month get from unix time, I'm sure you can do the rest or help explain more detail what you want to.
Hope this help
Here is the SQL how to create unix timestamp from your input :
SELECT
#year:= YEAR(CURDATE()),
#from:= CONCAT(#year, '-', 11, '-1') AS from_date,
#to:= CONCAT(IF(11<=2, #year, #year+1), '-', 2, '-1') AS to_date,
UNIX_TIMESTAMP(#from) AS from_timestamp,
UNIX_TIMESTAMP(#to) AS to_timestamp;
Month 11 & 2 are hard-coded. I used variables, so the sql is shorter.
SQL Fiddle.
Your scenario is :
With from_month as 11 and to_month as 2, is there a way where I can get a from value of 1383264000 and a to value of 1391212800 to match?
Which is from 2013-11-01 00:00:00 to 2014-02-01 00:00:00. Are you sure you need TO date to be from first of the month? or the last day of the Februar?
A solution was found that does an inclusive month check using months as integers from 0-11.
# Filter
SET #Fs = 2; # Mar
SET #Fe = 7; # Aug
#
SET #Ps = 2; # Mar
SET #Pe = 4; # May
SELECT
(
(
MOD(#Pe - #Fs + 24, 12) <= MOD(#Fe - #Fs + 24, 12)
)
AND (
MOD(#Ps - #Fs + 24, 12) <= MOD(#Pe - #Fs + 24, 12)
)
)
OR MOD(#Fe - #Fs + 24, 12)=11;

Range query from month and year

I have a table like this:
ID month year content
1 4 2013 xxxxx
2 5 2013 yyyyy
3 6 2013 zzzzz
4 8 2014 fffff
I want to query it based on a year and month range.
I have query like this:
SELECT * FROM UPP
WHERE ( month = '4' AND year = '2013' )
AND ( month = '6' AND year = '2013' )
That query runs but returns no result. Can anyone help me for fix this query?
NB: The month and year columns are integers.
Why not use the correct data type?
Failing that:
SELECT * FROM UPP WHERE (year=2013) AND (month BETWEEN 4 AND 6);
Would be the easiest path to this particular answer.
EDIT
SQL Fiddle for reference.
There will never be any rows where both month=4 and month=6 which is what your query is asking for. Adding brackets like that will not alter the AND behaviour as you seem to want them to so you are asking for WHERE year=2013 AND month=4 AND month=6.
There are a number of ways you could ask for what you seem to be wanting, for instance:
WHERE (year=2013 AND month=4) OR (year=2013 AND month=6)
or
WHERE year=2013 AND (month=4 OR month=6)
or
WHERE year=2013 AND month IN (4,6)
If you want the full range (the full quarter, months 4, 5, and 6 not just months 4 and 6 then swasheck's suggestion is probably the clearest way to go, though this will fall down if the date range straddles a boundary between years. If you need to do fully flexible ranged queries ("the six months to February 2013" and so forth) then you might want to rethink the table structure to more easily/efficiently support that.

SQL Server 2008 SP

I have two tables actual and forecast
Actual
month actual
6 20
7 60
8 70
and Forecast
month forecast
9 50
10 150
11 85
I have to update it in same column, i.e. till the data is available it should be updated from actual table and when data is not available there it should be updated from forecast table.
month actual/forecast
6 20
7 60
8 70
9 50
10 150
11 85
I'd do something like the following. The Source column is only for your test purposes. I'm also assuming that you might require some year or something included in your queries if your data spans multiple years.
SELECT month, actual as 'actual/forecast', 'A' as Source FROM Actual
UNION
SELECT month, forecast as 'actual/forecast', 'F' As Source FROM Forecast
WHERE month NOT IN (SELECT month FROM Actual)
maybe this will help
WITH T AS(
SELECT COUNT(*) FROM (SELECT month,actual FROM Actual
UNION ALL
SELECT MONTH,forecast FROM Forecast)
)
IF (SELECT COUNT(*) FROM T) = 0
BEGIN
--UPDATE here forecast TABLE
END
ELSE
--UPDATE here T table

conditional where clause mysql

I want to find in my MySQL table a specific time-period.
I want to find the period that
1. started in the past (cursusperiode_van < now())
2. and will last for at least 4 weeks (cursusperiode_tot - INTERVAL 28 DAY > now())
3. If i don't find this one, i would like to find the one that is the first to start after this one (cursusperiode_van > now() order by cursusperiode_van asc limit 1).
Periods do not overlap, at least not for more than 1 day.
Let's say i have
id:1 - period: 1 jan 2011 to 1 aug 2011
id:2 - period: 1 aug 2011 to 31 dec 2011
If i query that database on July 1, i want to find id 1, if i query on July 23, i want to find id 2.
Not likely to be relevant, but this query is to be embedded in another query like
select * from cursus where cursus_periode_id in (
select cursusperiode_id from cursusperiode_id where ...[panic!] ...
) and so on
I tried to use IFNULL, CASE, but i can't seem to get my head around it. How do i paste these two together?
Any hint or help is appreciated. Thank you.
You could UNION both queries, add a dummy column to aid in sorting and retain the first one found.
SQL Statement
SELECT c1.*
FROM cursus c1
INNER JOIN (
SELECT 1 AS Dummy
, cursusperiode_id
FROM cursusperiode_id
WHERE cursusperiode_tot - INTERVAL 28 DAY > now()
UNION ALL
SELECT 2 AS Dummy
, cursusperiode_id
FROM cursusperiode_id
WHERE cursusperiode_van > now()
) c2 ON c2.cursusperiod_id = c1.cursusperiod_id
ORDER BY
c2.Dummy
, c1.cursusperiode_van ASC
LIMIT 1