How to write a query that uses time attrition (e.g. within 1 day, within 2 days)? - sql

I 2 tables:
Enable
Disable
Each of those tables are tied to DateId to a date table that stores their dates.
How would I write a query so I can get rows in the disable table that disabled within 1, 2 or 3 days of having an entry in the enable table?
Thanks for the help

Use DATEDIFF! It works really well, and returns you the difference in dates.
select
*
from
disable d
inner join enable e on
d.Id = e.Id
where
datediff(day, e.DateId, d.DateId) between 1 and 3
You can also use it for any difference you'd like. DATEDIFF can also go backwards, so you could do datediff(day, d.DateId, e.DateId) between -3 and -1, if that struck your fancy.

Related

SQL Server : INSERT INTO within a SELECT

In SSRS 2008, my query is in the form
WITH CTE ( *that makes a list of dates, unfortunately only starting from today, not from whatever past date the report demands*)
SELECT
*a bunch of columns*
FROM
asimpletable p1
CROSS APPLY
(*CTE*) dates1
LEFT OUTER JOIN
(*demand list subquery with several selects pulling from several tables*) d1 ON (*simple.PartID AND CTE.demandDate*)
LEFT OUTER JOIN
(*supply list subquery with several selects pulling from several tables*) s1 ON (*yada*)
*..and 4 other joins to subqueries*
WHERE
*enough conditions that I don't want to copy it 6 times for 6 different INSERT INTO statements*
Because the CTE can only start from today, when I display all the select by date, any that was dated in the past are lumped out of order.
So I want to make a temp table to cross apply and feed in all the dates I need. I can see how to do it with 6 INSERT INTO, copying the joined subqueries, but not only would it look ugly, it would be worse to keep all the subqueries in sync.
I liked the sound of this, but can't see how to return the selects nor how to apply it to several subqueries https://stackoverflow.com/a/1101516/5612361.
Reason the CTE idea only starts from today:
SET #CTEStartDate = cast(getdate() as date);
SET #CTEEndDate = cast(dateadd(day,100,getdate()) as date);
WITH Dates(eaDate) AS (
SELECT #CTEStartDate AS eaDate --Select First day in range
UNION ALL
SELECT DATEADD(DAY, 1, eaDate) FROM Dates WHERE eaDate < #CTEEndDate --Add a record for every day in the range
)
When i try to use
SET #CTEStartDate = cast(dateadd(day,-30,getdate()) as date);
I get
For more information about this error navigate to the report server on
the local server machine, or enable remote errors
---------------------------- Cannot read the next data row for the dataset DataSet1. (rsErrorReadingNextDataRow)
---------------------------- An error has occurred during report processing. (rsProcessingAborted)
As soon as I take out that dateadd it resolves without a hitch.

SQL of a daterange without empty spaces

I need to generate a diagram out of data from a table. This table has the following date:
Timestamp | Value
01-20-2013| 5
01-21-2013| 7
01-22-2013| 3
01-25-2013| 5
As you can see not every date has a value. If I put that into a diagram it looks weird. Dates are used for the X-axis. As 01-23-2013 and 01-24-2013 is missing this values are either not printed in the diagram (looks weird) or the are printed put the line of the diagram goes from 3 directly to 5 and not to 0 as it should.
Is there a way via SQL to select the data so that it looks like this:
Timestamp | Value
01-20-2013| 5
01-21-2013| 7
01-22-2013| 3
01-23-2013| 0
01-24-2013| 0
01-25-2013| 5
Any help is appreciated!
Regards,
Alex
Edit: I had no clue that the database engine was that important. This is running on a MySQL 5 Database (not sure about the complete version string).
There are various ways to do this, depending on the database. Date functions are notoriously database independent.
Here is an approach using a "driver" table with all dates and to use this for a left outer join:
select driver.timestamp, coalesce(t.value, 0) as value
from (select distinct timestamp + n.n as timestamp
from t cross join
(select 0 as n union all select 1 union all select 2 union all select 3
)
) driver left outer join
t;
This version assumes that there are gaps of no more than three days.
In some databases, you can construct the list of dates using a recursive CTE. Such an approach would handle gaps of any size.

Running a query over past date ranges

I have a rather interesting problem which I first thought would be straight-forward, but it turned out to be more complicated.
I have data like this:
Date User ID
2012-10-11 a
2012-10-11 b
2012-10-12 c
2012-10-12 d
2012-10-13 e
2012-10-14 b
2012-10-14 e
... ...
Each row has a Date, User ID couple which indicates that that user was active on that day. A user can appear on multiple dates and a date will have multiple users -- just like in the example. I have millions of rows like this which cover a time range of about 90 days.
Here's the question: For each day, I want to get the number of users who have not been active for the past 10 days. For instance, if the user "a" was active on 2012-05-31 and but hasn't been active on any of the days between 06-01 and 06-10, I want to count this user on 6/10. I wouldn't count him again on the following days though unless he becomes active and disappears again.
Can I do this in SQL or would I need to some kind of script to organize the data the way I want. What would be your recommendations? I use Hive.
Thank you so much!
I think you can do this in Hive-compatible SQL. Here is the idea.
For each user/date get the next date for the user.
Discard the original record if the next is less than 10 days after the current one.
Add 10 to the date
Aggregate and count
I am not sure of all the Hive functions for things like date. Here is an example of how to do it:
select date+10, count(*)
from (select t.userid, t.date,
min(case when tnext.date > t.date then tnext.date end) as nextdate
from t left outer join
t tnext
on t.userid = tnext.userid
group by t.userid, t.date
) t
where nextdate is null or nextdate - date >= 10
group by date+10;
Note that the inner subquery would be better written using:
on t.userid = tnext.userid and t2.date > t.date
However, I don't know if Hive supports such a join (it doesn't support non-equijoins and it not clear about whether one or all clauses have to be equal).

To display only previous three months even the months before is not exist in database

Below is my new sql so far as i do not manage to use Dale M advice,
SELECT
all_months.a_month_id AS month,
year($P{date}) as year,
count(case when clixsteraccount.rem_joindate between DATE_FORMAT($P{date}-INTERVAL 2 MONTH, '%Y-%m-01') AND $P{date} THEN clixsteraccount.rem_registerbycn end) AS
total_activation,
'ACTIVATION(No)' AS fake_column
FROM clixsteraccount right join all_months on all_months.a_month_id = date_format(clixsteraccount.rem_joindate,'%m') and
(clixsteraccount.rem_registrationtype = 'Normal')and(clixsteraccount.rem_kapowstatus='pending' or clixsteraccount.rem_kapowstatus='success')
GROUP BY year,month
HAVING month BETWEEN month(date_sub($P{date},interval 2 month)) and month($P{date})
So, what i do is create a table with two fields, a_month_id(1,2,3...,12) and a_month(name of months). Sql above does give me what i want which is to display previous 3 months even the months before is not exist.
exp: data start on July. So, i want to display May,June and July data like 0,0,100.
The problem occur when it comes to next months or next year. When i try to generate sql based on parameter on Jan, it doesn't work like i thought. I do realize the problem are with 'Having' condition. Do anyone have idea how to improvised this sql to make it continue generate in the next,next year.
Thank you in advanced.
OK, I will make a few suggestions and give you an answer that will work on SQL Server - you will need to make any translations yourself.
I note that your query will aggregate all years together, i.e. Dec 2012 + Dec 2013 + Dec 2014 etc. Based on your question I don't think that is your intention so I will keep each distinct. You can change the query if that was your intention. I have also not included your selection criteria (other than by the month).
I suggest that you utilize an index table. This is a table stored in your database (or the master database if possible) with an clustered indexed integer column running from 0 to n where n is a sufficiently large number - 10,000 will be more than enough for this application (there are 12 months in a year so 10,000 represents 833 years). This table is so useful everyone should have one.
SELECT DATEADD(month, it.id, 0 ) AS month
,ISNULL(COUNT(clixsteraccount.rem_registerbycn), 0) AS registration
,'REGISTRATION(No)' AS fake_column
FROM cn
INNER JOIN ON ca.rem_registerbycn = cn.cn_id
clixsteraccount ca
RIGHT JOIN
IndexTable it ON it.id = DATEDIFF(month, 0, clixsteraccount.rem_joindate)
WHERE it.id BETWEEN DATEDIFF(month, 0, #StartDate) - 3 AND DATEDIFF(month, 0, GETDATE())
GROUP BY it.id
The way it works is by converting the clixsteraccount.rem_joindate to an integer that represents the number of months since date 0 (01-01-1900 in SQL Server). This is then matched to the id column of the IndexTable and limited by the dates you select. Because every number exists in the index table and we are using an outer join it doesn't matter if there are months missing from your data.

SQL query with week days

I would like to know what is the best way of creating a report that will be grouped by the last 7 days - but not every day i have data. for example:
08/01/10 | 0
08/02/10 | 5
08/03/10 | 6
08/04/10 | 10
08/05/10 | 0
08/06/10 | 11
08/07/10 | 1
is the only option is to create a dummy table with those days and join them altogether?
thank you
Try something like this
WITH LastDays (calc_date)
AS
(SELECT DATEADD(DAY, DATEDIFF(DAY, 0, CURRENT_TIMESTAMP) - 6, 0)
UNION ALL
SELECT DATEADD(DAY, 1, calc_date)
FROM LastDays
WHERE DATEADD(DAY, 1, calc_date) < CURRENT_TIMESTAMP)
SELECT ...
FROM LastDays l LEFT JOIN (YourQuery) t ON (l.cal_date = t.YourDateColumn);
Many people will suggest methods for dynamically creating a range of dates that you can then join against. This will certainly work but in my experience a calendar table is the way to go. This will make the SQL trivial and generic at the cost of maintaining the calendar table.
At some point in the future someone will come along and ask for another report that excludes weekends. You then have to make your dynamic days generation account for weekends. Then someone will ask for working-days excluding public-holidays at which point you have no choice but to create a calendar table.
I would suggest you bite the bullet and create a calendar table to join against. Pre-populate it with every date and if you want to think ahead then add columns for "Working Day" and maybe even week number if your company uses a non-standard week-number for reporting
You don't mention the specific language (please do for a more detailed answer), but most versions of sql have a function for the current date (GetDate(), for instance). You could take that date, subtract x (7) days and build your WHERE statement like that.
Then you could GROUP BY the day-part of that date.
select the last 7 transactions and left join it with your query and then group by the date column. hope this helps.