I noticed something in our production code and it really threw me off. We have a data table that has the year that someone was in contact with us. Lets say it goes back to the late 90s.
We have a query that calculates the previous year to use in other future calculations within the same query. It looks like this:
#CurrYear = 2016
select #PrevYear = max(Year)
from x
where x.Year < #CurrYear
This seems like a very convoluted way...not sure why this person didnt do:
#PrevYear = #CurrYear - 1
I'm asking this more in a query performance standpoint. Which is faster? Why so much code to calculate previous year?
I believe he either didn't think of another way to do it, or he wanted #PrevYear to represent the last year that you'd been contacted (Lets say there is no data from 2015, then #PrevYear should be 2014 .
In terms of performance, your suggestion should be faster as it is a calculation on parameters. It's hard to say if there will be any difference between them, depends on the size of the table, indexes and etc.
Related
Dear Stackoverflow Nation,
this post is related to the performance of the query.
I execute a simple query as below
select wonum, siteid from workorder where workorder.siteid= 'MCT' and istask=0
and decode(workorder.pmnum,null,workorder.reportdate,workorder.targstartdate) >= '12-MAR-18' and
decode(workorder.pmnum,null,workorder.reportdate,workorder.targstartdate) <= '14-MAR-18';
It executed perfectly , took 6 sec
as i added one more condition type ='MAINTENANCE' ,query took 28 sec
select wonum, siteid from workorder where workorder.siteid= 'MCT' and istask=0 and type ='MAINTENANCE'
and decode(workorder.pmnum,null,workorder.reportdate,workorder.targstartdate) >= '12-MAR-18' and
decode(workorder.pmnum,null,workorder.reportdate,workorder.targstartdate) <= '14-MAR-18'; --28.73
As I know ,I need to create an index on workorder table ,
but I am unable to figure out on which field ,I need to create an index and how it helps to run query fast.
(Note:there is an index (ind_1 - with attributes wonum,siteid ) already in system
kindly help.Apologize if its a basic question for PRos
Generally speaking create indexes on columns involved in where clause. As you described it, indexing the type column might help.
Will it really help? Who knows ... check explain plan. Collect statistics for the table so that Optimizer knows what to do (i.e. chooses the best execution plan). Then you might be able to figure out what to do.
Moreover, it seems that you're forcing Oracle to perform implicit conversions. Saying that
some_date >= '12-mar-18'
means that - if some_date column's datatype is date (looks like it is; otherwise you'd get wrong result) - Oracle has to convert a string '12-mar-18' into a valid date by applying correct format mask (such as dd-mon-yy). Why would you want to do that? Provide date value yourself!
some_date >= date '2018-03-12'
or
some_date >= to_date('12-mar-18', 'dd-mon-yy')
But beware; mar means "March". This query would certainly fail in my database which speaks Croatian, and we don't have any mar months here (it is ožu). Perhaps you'd rather stick to numerics here, i.e. 12-03-18. One more note: this value is difficult to understand; what is 12? Is it 12th day in the month, or is it December? The same goes for 03. Therefore, always use values that cause no confusion, either by providing date literals (which are always in yyyy-mm-dd format - the one I suggested first), or use to_date function with appropriate format mask.
in Salesforce, how to create a formula that calculate the highest figure for last month? for example, if I have an object that keeps records that created in Sept, now would like to calculate its max value (in this case, should be 20 on 3/8/2019) in last month's (August). If it's in July, then need to calculate for June. How to construct the right formula expression? Thanks very much!
Date Value
1/9/2019 10
1/8/2019 14
2/8/2019 15
3/8/2019 20
....
30/8/2019 15
You can't do this with normal formulas on records because they "see" only current records (and some related via lookup), not other rows in same table.
You could make another object called "accounting periods" or something like that. Link all these entries to periods (months) in master-detail relationship. You'll then be able to use rollup summary with MAX(). Still not great because you need lookup to previous month to pull it but should give you an idea.
You could make a report that achieves something like that. PREVGROUPVAL will let you do some amazing & scary stuff. https://trailhead.salesforce.com/en/content/learn/projects/rd-summary-formulas/rd-compare-groups Then... if all you need is a report - great. If you really need it saved somewhere - you could look into reporting snapshots & save results in helper object...
If you want to do it without any data model changes like that master-detail or helper object - you could also write some code. Nightly batch job (running daily? only on 1st day of month?) should be pretty simple.
Without code - in a pinch you could make a Flow that queries records from previous month. Bit expensive to run such thing for August every time you add a September record but if you discarded other options...
I need to find expired credit cards in a table. The fields expire_year and expire_month are integer values.
I was thinking something like this could work:
select *
from CREDITCARD
where CURRENT_TIMESTAMP > DATEFROMPARTS(EXPIRE_YEAR, EXPIRE_MONTH, 1);
The problem with this is that the definition of expired would be the first day of the next month. Therefore I need to find a way to write EXPIRE_MONTH + 1. But this is also no good, as the month might be December, in which case I'd be looking for month number 13. In such cases, I'd need to bump the EXPIRE_YEAR instead, and set EXPIRE_MONTH to 1.
I´ve tried to google to the solution, but my issue seems a bit too specific. In Java this would be easy enough to solve, but my SQL knowledge is limited to fairly simple queries.
Something like that :
SELECT DATEADD(month, 1, DATEFROMPARTS(EXPIRE_YEAR, EXPIRE_MONTH, 1))FROM MY_TABLE
I'm working with a bit of an older code base where conciseness is not found in many places.
One piece of code we are constantly using in the database is to determine if two dates are within the same program year. For instance, Program Year 2011 begins on July 1st, 2011 and ends on July 1st, 2012(or technically the day before)
The usual way I see that this problem is solved is by using this kind of code:
if Month(#EnrollmentDate)>=7 begin
set #StartDate='07/01/'+LTRIM(RTRIM(Year(#EnrollmentDate)))
set #EndDate='07/01/'+LTRIM(RTRIM(Year(#EnrollmentDate)+1))
end else begin
set #StartDate='07/01/'+LTRIM(RTRIM(Year(#EnrollmentDate)-1))
set #EndDate='07/01/'+LTRIM(RTRIM(Year(#EnrollmentDate)))
end
...
where (ENROLLMENTDATE >= #StartDate and ENROLLMENTDATE < #EndDate)
I recently happened to have to solve this problem and the instant thing that popped in my head was something much more concise:
where year(dateadd(mm,-6,ENROLLMENTDATE)) = year(dateadd(mm,-6,#EnrollmentDate))'
Before I go introducing new bugs into a system that "just works", I'd like to ask SO about this. Are these two pieces of code exactly the same? Will they always give the same output(assuming valid dates)?
the problem I see is that, depending on the optimizer, your solution (that looks better) may not use an index defined on ENROLLMENTDATE since you're doing operations on it, while the original solution would. If there are no indexes on that field, then I don't see an issue
An even easier way to handle this situation is to create a calendar table with a column for the program year. Then there is no logic at all, you just query the values and compare them:
if
(select ProgramYear from dbo.Calendar where BaseDate = #StartDate) =
(select ProgramYear from dbo.Calendar where BaseDate = #EndDate)
begin
-- do something
end
There are many posts on this site about creating calendar tables and using them for many different purposes. In my experience, using a table in this way is always clearer and more maintainable than creating formulas in code.
As it happens, December has 31 days, so no matter how many months you need to subtract, you will always be able to align the resulting date range with a whole year, and therefore your expression will always be true whenever the original one was, even in the general case for an enrolment year starting on other dates.
I once used a dialect of SQL with a range of date manipulation functions that made this slightly easier than string twiddling, something like this:
WHERE enrolmentdate >= #YearBeg(:enrolmentdate + 6 MONTHS) - 6 MONTHS
AND enrolmentdate < #YearBeg(:enrolmentdate + 6 MONTHS) + 6 MONTHS
The problem that I have is SQL Server Reporting Services does not like Sum(First()) notation. It will only allow either Sum() or First().
The Context
I am creating a reconciliation report. ie. what sock we had a the start of a period, what was ordered and what stock we had at the end.
Dataset returns something like
Type,Product,Customer,Stock at Start(SAS), Ordered Qty, Stock At End (SAE)
Export,1,1,100,5,90
Export,1,2,100,5,90
Domestic,2,1,200,10,150
Domestic,2,2,200,20,150
Domestic,2,3,200,30,150
I group by Type, then Product and list the customers that bought that product.
I want to display the total for SAS, Ordered Qty, and SAE but if I do a Sum on the SAS or SAE I get a value of 200 and 600 for Product 1 and 2 respectively when it should have been 100 and 200 respectively.
I thought that i could do a Sum(First()) But SSRS complains that I can not have an aggregate within an aggregate.
Ideally SSRS needs a Sum(Distinct())
Solutions So Far
1. Don't show the Stock at Start and Stock At End as part of the totals.
2. Write some code directly in the report to do the calc. tried this one - didn't work as I expected.
3. Write an assembly to do the calculation. (Have not tried this one)
Edit - Problem clarification
The problem stems from the fact that this is actually two reports merged into one (as I see it). A Production Report and a sales report.
The report tried to address these criteria
the market that we sold it to (export, domestic)
how much did we have in stock,
how much was produced,
how much was sold,
who did we sell it to,
how much do we have left over.
The complicating factor is the who did we sell it to. with out that, it would have been relativly easy. But including it means that the other top line figures (stock at start and stock at end) have nothing to do with the what is sold, other than the particular product.
I had a similar issue and ended up using ROW_NUMBER in my query to provide a integer for the row value and then using SUM(IIF(myRowNumber = 1, myValue, 0)).
I'll edit this when I get to work and provide more data, but thought this might be enough to get you started. I'm curious about Adolf's solution too.
Pooh! Where's my peg?!
Have you thought about using windowing/ranking functions in the SQL for this?
This allows you to aggregate data without losing detail
e.g. Imagine for a range of values, you want the Min and Max returning, but you also wish to return the initial data (no summary of data).
Group Value Min Max
A 3 2 9
A 7 2 9
A 9 2 9
A 2 2 9
B 5 5 7
B 7 5 7
C etc..
Syntax looks odd but its just
AggregateFunctionYouWant OVER (WhatYouWantItGroupedBy, WhatYouWantItOrderedBy) as AggVal
Windowing
Ranking
you're dataset is a little weird but i think i understand where you're going.
try making the dataset return in this order:
Type, Product, SAS, SAE, Customer, Ordered Qty
what i would do is create a report with a table control. i would set up the type, product, and customer as three separate groups. i would put the sas and sae data on the same group as the product, and the quantity on the customer group. this should resemble what i believe you are trying to go for. your sas and sae should be in a first()
Write a subquery.
Ideally SSRS needs a Sum(Distinct())
Re-write your query to do this correctly.
I suspect your problem is that you're written a query that gets you the wrong results, or you have poorly designed tables. Without knowing more about what you're trying to do, I can't tell you how to fix it, but it has a bad "smell".