I have the following query.
UPDATE t
SET UnitsSold = sub.UnitsSold,
FROM dbo.table1 t
JOIN (SELECT s.CustomerKey,
s.WeekKey,
s.ProductKey,
Sum(s.UnitsSold) AS [UnitsSold],
FROM dbo.table2 s
WHERE WeekKey >= 335
GROUP BY s.WeekKey,
s.CustomerKey,
s.ProductKey) AS sub
ON sub.WeekKey = t.WeekKey
AND sub.CustomerKey = t.CustomerKey
AND sub.ProductKey = t.ProductKey
It runs like a champ. About 2 seconds. Then when try and make it dynamic via the following.
DECLARE #StartWeekKey AS INT
SET #StartWeekKey = 335
UPDATE t
SET UnitsSold = sub.UnitsSold,
FROM dbo.table1 t
JOIN (SELECT s.CustomerKey,
s.WeekKey,
s.ProductKey,
Sum(s.UnitsSold) AS [UnitsSold],
FROM dbo.table2 s
WHERE WeekKey >= #StartWeekKey
GROUP BY s.WeekKey,
s.CustomerKey,
s.ProductKey) AS sub
ON sub.WeekKey = t.WeekKey
AND sub.CustomerKey = t.CustomerKey
AND sub.ProductKey = t.ProductKey
All of a sudden, it's super slow.
any good ideas?
EDIT:
Probalby should have mentioned this, but this is contained in a stored proc.
Added the #StartWeekKey as a parameter to the proc and it goes back to running in a few seconds.
This question seems to have been asked several times before and the general answer is that it has to do with statistics.
Try:
UPDATE STATISTICS table_or_indexed_view_name
to get your statistics up to date and see if that makes a difference.
That isn't unheard of when different parameters have very different distributions, thus different good plans. What can happen is that the query gets executed for a given value, and then that plan gets cached and re-used inappropriately for a different value.
If this is the case (just a guess - I can't run your query to check!) then try adding:
OPTION (OPTIMIZE FOR (#StartWeekKey UNKNOWN))
to the end of the query.
Another thought: is WeekKey actually an int ? is this some kind of mass type conversion issue?
I have no way of checking these; if I'm miles off the track, let me know so I can remove an unhelpful answer.
Related
I have the following problem with formatting a PostgreSQL query in Datagrip (and also in all other JetBrains products regarding PG queries):
update some_table
set some_column = 42
where id = 42
returning val_a as "valA",
val_b as "valB",
val_c as "valC",
val_d as "valD",
val_e as "valE",
val_f as "valF",
val_g as "valG";
When I now use the built-in SQL formatter, DataGrip produces this:
update some_table
set some_column = 42
where id = 42
returning val_a as "valA", val_b as "valB", val_c as "valC", val_d as "valD", val_e as "valE", val_f as "valF", val_g as "valG";
You see what the issue is: All returned values are in one line (it even ignores my max-line-length setting). I tried different settings in my IDE but to no avail. Note that for now, I don't care if the returned vals are indented or aligned or whatever, I just want the query to be "readable".
Looking forward to a solution and thanks in advance!
There is no option for that :( Please, file a request
https://youtrack.jetbrains.com/newIssue?project=DBE
Some context before the question.
Imagine file FileA having around 50 fields of different types. Instead of all programs using the file, I tried having a service program, so the file could only be accessed by that service program. The programs calling the service would then receive a DataStructure based on the file structure, as an ExtName. I use SQL to recover the information, so, basically, the procedure would go like this :
Datastructure shared by service program :
D FileADS E DS ExtName(FileA) Qualified
Procedure called by programs :
P getFileADS B Export
D PI N
D PI_IDKey 9B 0 Const
D PO_DS LikeDS(FileADS)
D LocalDS E DS ExtName(FileA) Qualified
D NullInd S 5i 0 Array(50) <-- Since 50 fields in fileA
//Code
Clear LocalDS;
Clear PO_DS;
exec sql
SELECT *
INTO :LocalDS :nullind
FROM FileA
WHERE FileA.ID = :PI_IDKey;
If SqlCod <> 0;
Return *Off;
EndIf;
PO_DS = LocalDS;
Return *On;
P getFileADS E
So, that procedure will return a datastructure filled with a record from FileA if it finds it.
Now my question : Is there any way I can assign the %nullind(field) = *On without specifying EACH 50 fields of my file?
Something like a loop
i = 1;
DoW (i <= 50);
if nullind(i) = -1;
%nullind(datastructure.field) = *On;
endif;
i++;
EndDo;
Cause let's face it, it'd be a pain to look each fields of each file every time.
I know a simple chain(n) could do the trick
chain(n) PI_IDKey FileA FileADS;
but I really was looking to do it with SQL.
Thank you for your advices!
OS Version : 7.1
First, you'll be better off in the long run by eliminating SELECT * and supplying a SELECT list of the 50 field names.
Next, consider these two web pages -- Meaningful Names for Null Indicators and Embedded SQL and null indicators. The first shows an example of assigning names to each null indicator to match the associated field names. It's just a matter of declaring a based DS with names, based on the address of your null indicator array. The second points out how a null indicator array can be larger than needed, so future database changes won't affect results. (Bear in mind that the page shows a null array of 1000 elements, and the memory is actually relatively tiny even at that size. You can declare it smaller if you think it's necessary for some reason.)
You're creating a proc that you'll only write once. It's not worth saving the effort of listing the 50 fields. Maybe if you had many programs using this proc and you had to create the list each time it'd be a slight help to use SELECT *, but even then it's not a great idea.
A matching template DS for the 50 data fields can be defined in the /COPY member that will hold the proc prototype. The template DS will be available in any program that brings the proc prototype in. Any program that needs to call the proc can simply specify LIKEDS referencing the template to define its version in memory. The template DS should probably include the QUALIFIED keyword, and programs would then use their own DS names as the qualifying prefix. The null indicator array can be handled similarly.
However, it's not completely clear what your actual question is. You show an example loop and ask if it'll work, but you don't say if you had a problem with it. It's an array, so a loop can be used much like you show. But it depends on what you're actually trying to accomplish with it.
for old school rpg just include the nulls in the data structure populated with the select statement.
select col1, ifnull(col1), col2, ifnull(col2), etc. into :dsfilewithnull where f.id = :id;
for old school rpg that can't handle nulls remove them with the select statement.
select coalesce(col1,0), coalesce(col2,' '), coalesce(col3, :lowdate) into :dsfile where f.id = :id;
The second method would be easier to use in a legacy environment.
pass the key by value to the procedure so you can use it like a built in function.
One answer to your question would be to make the array part of a data structure, and assign *all'0' to the data structure.
dcl-ds nullIndDs;
nullInd Ind Dim(50);
end-ds;
nullIndDs = *all'0';
The answer by jmarkmurphy is an example of assigning all zeros to an array of indicators. For the example that you show in your question, you can do it this way:
D NullInd S 5i 0 dim(50)
/free
NullInd(*) = 1 ;
Nullind(*) = 0 ;
*inlr = *on ;
return ;
/end-free
That's a complete program that you can compile and test. Run it in debug and stop at the first statement. Display NullInd to see the initial value of its elements. Step through the first statement and display it again to see how the elements changed. Step through the next statement to see how things changed again.
As for "how to do it in SQL", that part doesn't make sense. SQL sets the values automatically when you FETCH a row. Other than that, the array is used by the host language (RPG in this case) to communicate values back to SQL. When a SQL statement runs, it again automatically uses whatever values were set. So, it either is used automatically by SQL for input or output, or is set by your host language statements. There is nothing useful that you can do 'in SQL' with that array.
In this example, I have two tables; Order Header (oe-hdr) and Location (location). The order header table contains two fields (sale-location-key and ship-location-key) which have an associated location name in the location table. If I were to use SQL to get my results, I would do something like this..
SELECT oe-hdr.order-num, oe-hdr.order-date, saleloc.location-name, shiploc.location-name
FROM oe-hdr,
(SELECT location.location-name
FROM oe-hdr, location
WHERE oe-hdr.sale-location-key = location-key) as saleloc,
(SELECT location.location-name
FROM oe-hdr, location
WHERE oe-hdr.ship-location-key = location-key) as shiploc
WHERE oe-hdr.order-num = saleloc.order-num
AND oe-hdr.order-num = shiploc.order-num
Does anyone know how to replicate this in a Progress procedure?
Define two buffers for "location" and then do a for-each with a link to the buffers:
DEFINE BUFFER saleloc FOR location.
DEFINE BUFFER shiploc FOR location.
FOR EACH oe-hdr
NO-LOCK,
EACH saleloc
WHERE saleloc.location-key = oe-hdr.sale-location-key
NO-LOCK,
EACH shiploc
WHERE shiploc.location-key = oe-hdr.ship-location-key
NO-LOCK
:
DISPLAY
oe-hdr.order-num
oe-hdr.order-date
saleloc.location-name
shiploc.location-name
DOWN
.
END.
one note - if the sale or ship-to doesn't exist in the location table, then the entire record will not be displayed. You'll need a different approach if you need that functionality - it'll involve moving the "linking" to a pair of "FIND" statements in the FOR EACH block.
To overcome Tims point about the missing addresses you could have a function or method (if using OO code) that returns the location-name and use that in the display. It would allow for better error handling on that front. Not sure the performance impact though.
Just a thought.
I tried to grab the latest N records with a unique value (first_name).
So far:
#users = User.all(:limit => 5, :sort => [:created_at, :desc]).distinct(:first_name)
almost works..But ignores the limit and sort order
Also:
#users = User.limit(5).desc(:created_at).distinct(:first_name)
Ignores both 'limit' and 'desc'
#users = User.limit(5)
Works..
What am I doing wrong?
Any help would be greatly appreciated!
I played with this for a little while and this is the best I could come up with.
Good luck.
#users = User.desc(:created_at).reduce([]) do |arr, user|
unless arr.length == 5 || arr.detect{ |u| u.first_name == user.first_name }
arr << user
end
arr
end
Have you tried using a pagination gem such as amatsuda / kaminari and limiting the results using page().per()?
Both distinct and count ignore the limit command in Mongoid. With count you can pass true (i.e. User.limit(5).count(true)) to force it to pay attention to the scope. Unfortunately there is no such trick for distinct as far as I'm aware (see docs/source here).
If you want to just grab the first 5 first_name's you can do this (not distinct):
User.desc(:created_at).limit(5).map(&:first_name)
This will respect the limit, but still load 5 full objects from the database (then discard the rest of the object to give you full name). If you actually need to run distinct, you're better off heading toward an aggregation framework solution.
I haven't tested, but this seems to be what you're looking for: https://stackoverflow.com/a/17568267/127311
I played with some result I found this.
User.limit(2).count => 10 #but in array I found only two results
User.limit(2).to_a.count => 2
May be limit gives the correct result, but count query gives wrong result.
The following query fails to load the tables when I execute it:
IEnumerable<Bookmark> tempBookmarks = ListBookmarksByUserID(userID);
IEnumerable<CandidateWithBookmarks> results = (from c in _internshipEntities.CandidateSet
.Include("education")
.Include("progress")
.Include("contacts")
.Include("availability")
.Include("hosttypes")
.Include("hostsizes")
.Include("hostcapacities")
.Include("hoststates")
.Include("users")
join b in tempBookmarks on c.ID equals b.candidates.ID
select new CandidateWithBookmarks()
{CandidateObject = c, BookmarkObject = b});
return results;
I have found some articles related to the problem, namely Alex James' article "How to make Include really Include". The solution comes with one caveat:
For this to work your final select must be entities, i.e. select post rather than select new {…}
Which is obviously an issue for the above block of code. Are there any other known work-arounds for this issue that won't break eager loading?
I think I solved this but it might only work for this specific instance, by moving the includes after the join, the query appears to work:
IEnumerable<CandidateWithBookmarks> results = (
from b in tempBookmarks
join c in _internshipEntities.CandidateSet
.Include("education")
.Include("progress")
.Include("contacts")
.Include("availability")
.Include("hosttypes")
.Include("hostsizes")
.Include("hostcapacities")
.Include("hoststates")
.Include("users")
on b.candidates.ID equals c.ID
select new CandidateWithBookmarks(){CandidateObject = c, BookmarkObject = b});
Edit: Another query I have similar to this requires an outer join as well, which creates some issues since it then matters what you join to what, unlike this example, but it's still doable.