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

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.

Related

SQL Server: compare only month and day - SARGable

I have a table storing a datetime column, which is indexed. I'm trying to find a way to compare ONLY the month and day (ignores the year totally).
Just for the record, I would like to say that I'm already using MONTH() and DAY(). But I'm encountering the issue that my current implementation uses Index Scan instead of Index Seek, due to the column being used directly in both functions to get the month and day.
There could be 2 types of references for comparison: a fixed given date and today (GETDATE()). The date will be converted based on time zone, and then have its month and day extracted, e.g.
DECLARE #monthValue DATETIME = MONTH(#ConvertDateTimeFromServer_TimeZone);
DECLARE #dayValue DATETIME = DAY(#ConvertDateTimeFromServer_TimeZone);
Another point is that the column stores datetime with different years, e.g.
1989-06-21 00:00:00.000
1965-10-04 00:00:00.000
1958-09-15 00:00:00.000
1965-10-08 00:00:00.000
1942-01-30 00:00:00.000
Now here comes the problem. How do I create a SARGable query to get the rows in the table that match the given month and day regardless of the year but also not involving the column in any functions? Existing examples on the web utilise years and/or date ranges, which for my case is not helping at all.
A sample query:
Select t0.pk_id
From dob t0 WITH(NOLOCK)
WHERE ((MONTH(t0.date_of_birth) = #monthValue AND DAY(t0.date_of_birth) = #dayValue))
I've also tried DATEDIFF() and DATEADD(), but they all end up with an Index Scan.
Adding to the comment I made, on a Calendar Table.
This will, probably, be the easiest way to get a SARGable query. As you've discovered, MONTH([YourColumn]) and DATEPART(MONTH,[YourColumn]) both cause your query to become non-SARGable.
Considering that all your columns, at least in your sample data, have a time of 00:00:00 this "works" to our advantage, as they are effectively just dates. This means we can easily JOIN onto a Calendar Table using something like:
SELECT dob.[YourColumn]
FROM dob
JOIN CalendarTable CT ON dob.DateOfBirth = CT.CalendarDate;
Now, if we're using the table from the above article, you will have created some extra columns (MonthNo and CDay, however, you can call them whatever you want really). You can then add those columns to your query:
SELECT dob.[YourColumn]
FROM dob
JOIN CalendarTable CT ON dob.DateOfBirth = CT.CalendarDate
WHERE CT.MonthNo = #MonthValue
AND CT.CDay = #DayValue;
This, as you can see, is a more SARGable query.
If you want to deal with Leap Years, you could add a little more logic using a CASE expression:
SELECT dob.[YourColumn]
FROM dob
JOIN CalendarTable CT ON dob.DateOfBirth = CT.CalendarDate
WHERE CT.MonthNo = #MonthValue
AND CASE WHEN DATEPART(YEAR, GETDATE()) % 4 != 0 AND CT.CDat = 29 AND CT.MonthNo = 2 THEN 28 ELSE CT.Cdat END = #DayValue;
This treats someone's birthday on 29 February as 28 February on years that aren't leap years (when DATEPART(YEAR, GETDATE()) % 4 != 0).
It's also, probably, worth noting that it'll likely be worth while changing your DateOfBirth Column to a date. Date of Births aren't at a given time, only on a given date; this means that there's no implicit conversion from datetime to date on your Calendar Table.
Edit: Also, just noticed, why are you using NOLOCK? You do know what that does, right..? Unless you're happy with dirty reads and ghost data?

Group data from 3 tables and sort by week ending date

Here is the problem I am stuck on.
I have three tables which log the dates and locations customers visit my sales centers. One table is the initial visit, the second is a able for repeat visits, of which there could be many and the third is a deposit table which stores the data related to when people leave a deposit and on which job they left the deposit.
So I need to sort all do this data by the week ending date, which I do have working on one table at a time. So for example, anyone who visited my sales center on March 3 would be counted in as traffic for the week ending march 9.
Now I would like to query the data so that I call pull the dates from the other two tables as well and have them sort by week ending along with the other data.
So my final output would look like this:
Week ending | initial visit | bback | deposit
3/9/2014 9 3 0
3/16/2104. 12. 0. 1
My tables structure looks like this:
Salescenter_clientinfo
Initial_visit (date)
Community
Customer_bback
Bback_date
Bback_community
Customer Deposit
Deposit_date
Doposit_community
All of these tables also have a field which is the customer_id which links them all.
As stated earlier, I do have the sql working which gets the job done using one table but how do I accomplish this on multiple tables?
I forgot to mention, I run this sql from an excel pivot table against an MSSql database. I am using excel to do the pivot and be the final report.
Any help in very much appreciated.
Thanks in advance.
Steve
If I'm understanding your original question correctly, you're trying to track everything that happened at your center, in each category, during a specific week. So if a customer's initial visit was during week 1, and he/she came back during week 2, those two items would fall under different weeks.
Based on that assumption, the linkage on Customer_ID is actually a bit of a red herring: you want data aggregated by week, and don't care if it all reflects the same customer's initial visit.
If so, then the following SQL might do what you are looking for:
Set Datefirst 1;
SELECT
ISNULL(vc.WeekEnding, ISNULL(b.WeekEnding, d.WeekEnding)) WeekEnding,
vc.InitialVisit,
b.BBack,
d.Deposit
FROM
(
SELECT
DATEADD(dd, 7 - DATEPART(DW, initial_visit), initial_visit) AS WeekEnding,
COUNT(initial_visit) AS InitialVisit
FROM dbo.SalesCenter_clientinfo
GROUP BY DATEADD(dd, 7 - DATEPART(DW, initial_visit), initial_visit)
) vc
FULL OUTER JOIN
(
SELECT
DATEADD(dd, 7 - DATEPART(DW, Bback_date), Bback_date) AS WeekEnding,
COUNT(Bback_date) AS BBack
FROM dbo.Customer_bback
GROUP BY DATEADD(dd, 7 - DATEPART(DW, Bback_date), Bback_date)
) b ON
vc.WeekEnding = b.WeekEnding
FULL OUTER JOIN
(
SELECT
DATEADD(dd, 7 - DATEPART(DW, Deposit_date), Deposit_date) AS WeekEnding,
COUNT(Deposit_date) AS Deposit
FROM dbo.Customer_deposit
GROUP BY DATEADD(dd, 7 - DATEPART(DW, Deposit_date), Deposit_date)
) d ON
vc.WeekEnding = d.WeekEnding
Fiddle

using datepart() with the group by command

I am beating my head against something that is, I'm sure, very obvious -
I have a bit of SQL code designed to sum the total selling price of each invoice in my store and then organize it by Month.
select SUM(totalsellingprice) from dbo.tblServiceOrders
where datepart(MONTH,dbo.tblServiceOrders.datereceived) =12
As far as I understand it, that should return the sum of all the totatlsellingprice from month 12 (December). Currently, this query returns
135998.92
However, if I then try to put that into a group by to get it to spit it out for all months, the number changes.
select SUM(totalsellingprice) from dbo.tblServiceOrders
group by datepart(MONTH,dbo.tblServiceOrders.datereceived)
And I get this table -
1 - 110567.70
2 - 60059.59
3 - 135998.92
4 - 63089.22
5 - 102287.01
6 - 71088.68
7 - 149102.10
8 - 67722.65
9 - 67122.45
10 - 64234.82
11 - 7542.05
12 - 130461.10
There are 12 rows, which sounds good to me (12 months in a year) but the last row is 130461.
How is it possible that row 12 from the second search does not equal what I did in the first search? I feel like I'm missing something obvious but I can't for the life of me figure out what.
Any and all help will be much appreciated!
I got it:
Your query is very confusing since it does not include the MONTH column:
If you would have done that, you would have realized your query is not ordered by MONTH and so, the MONTH 12 is returned as the 3rd row of your query.
;)
select SUM(totalsellingprice) from dbo.tblServiceOrders
group by datepart(MONTH,dbo.tblServiceOrders.datereceived)
order by datepart(MONTH,dbo.tblServiceOrders.datereceived)
And please, don't refer to the row index to choose which month is related to which sum. And should be a good idea to also discriminate the year (if you need to).
Run this and see what it does...
select dateadd(month, datediff(month, 0, datereceived), 0),
Sum(totalsellingprice)
from dbo.tblServiceOrders
group by dateadd(month, datediff(month, 0, datereceived), 0)

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.

Confused on count(*) and self joins

I want to return all application dates for the current month and for the current year. This must be simple, however I can not figure it out. I know I have 2 dates for the current month and 90 dates for the current year. Right, Left, Outer, Inner I have tried them all, just throwing code at the wall trying to see what will stick and none of it works. I either get 2 for both columns or 180 for both columns. Here is my latest select statement.
SELECT count(a.evdtApplication) AS monthApplicationEntered,
count (b.evdtApplication) AS yearApplicationEntered
FROM tblEventDates a
RIGHT OUTER JOIN tblEventDates b ON a.LOANid = b.loanid
WHERE datediff(mm,a.evdtApplication,getdate()) = 0
AND datediff(yy,a.evdtApplication, getdate()) = 0
AND datediff(yy,b.evdtApplication,getdate()) = 0
You don't need any joins at all.
You want to count the loanID column from tblEventDates, and you want to do it conditionally based on the date matching the current month or the current year.
SO:
SELECT SUM( CASE WHEN Month(a.evdtApplication) = MONTH(GEtDate() THEN 1 END) as monthTotal,
count(*)
FROM tblEventDates a
WHERE a.evdtApplication BETWEEN '2008-01-01' AND '2008-12-31'
What that does is select all the event dates this year, and add up the ones which match your conditions. If it doesn't match the current month it won't add 1. Actually, don't even need to do a condition for the year because you're just querying everything for that year.