Operations on selected items - sql

I want to have query like this:
SELECT
sum(data_parts.size) as size_sum,
(size_sum/2.) as half_of_size_sum
FROM
data_parts
WHERE
data_parts.some_id='1';
Of course it won't work, because there's no column named size_sum, so my question is:
Is there a way to use size_sum as a parameter in next select item?

Other than using a subquery containing your current query (see Davide's answer), I don't think there is a way to do that.
But you could always do:
SELECT
sum(data_parts.size) as size_sum,
(sum(data_parts.size)/2.) as half_of_size_sum
FROM
data_parts
WHERE
data_parts.id='1';
Postgres is smart enough to only get that sum once. Only if this query will be greatly expanded with more calculations being done on size_sum would I recommend the subquery approach. Not because it works better, but because it will be easier to read. But if the current calculation is all you need, don't bother with a subquery. It would actually be less easy to read.

yes, a (somewhat) ugly way of making the query run there is...
SELECT
SIZE_SUM,
SIZE_SUM/2 AS HALF_OF_SIZE_SUM
FROM (
SELECT
sum(data_parts.size) as size_sum)
FROM
data_parts
WHERE
data_parts.id='1') X;
But I don't think there is a way on postgre to do operations directly based on the previous select fields

No, and too unnecesary to use subselects for this, simply use SUM(data_parts.size) again.

I'm not sure whether I catch your point.
Do you want the code like this?
SELECT
sum(data_parts.size) as size_sum,
(sum(data_parts.size)/2.) as half_of_size_sum
FROM
data_parts
WHERE
data_parts.some_id='1';

Related

Measure cost of SELECT statement in PL/SQL

I'm trying to measure how much does a select cost. Normally I would do it simply by getting the value of consistent gets before executing the statement and after, however, I'd like to wrap it in PL/SQL so that it's more generic. The problem I'm facing however, is how can I simply run a select without actually storing the result?
One of the ideas I had was to wrap it in a for loop like so
select value into getsBefore from v$sesstat ...
for dummy in (select name from my_table where city = 'XYZ') loop
i := i;
end loop;
select value into getsAfter from v$sesstat ...
But this doesn't seem like a correct approach.
I guess that I could simplify my question to - how can I SELECT something INTO <nothing>?
Any ideas or hints on how to accomplish this?
Thanks
Try : SELECT * bulk collect INTO [variable]
See this as a reference SELECT INTO Statement
Since the counters in V$SESSSTAT are absolute since a session is started there is no other way than to store a snapshot before an action and then subtract the snapshot from new one after the action is executed.
One of Tom Kyte's most used scripts is RUNSTATS meant to compare two actions performing the same thing to see which implementation is better.
You can find of the versions with description at http://betteratoracle.com/posts/12-runstats

How to simulate ActiveRecord Model.count.to_sql

I want to display the SQL used in a count. However, Model.count.to_sql will not work because count returns a FixNum that doesn't have a to_sql method. I think the simplest solution is to do this:
Model.where(nil).to_sql.sub(/SELECT.*FROM/, "SELECT COUNT(*) FROM")
This creates the same SQL as is used in Model.count, but is it going to cause a problem further down the line? For example, if I add a complicated where clause and some joins.
Is there a better way of doing this?
You can try
Model.select("count(*) as model_count").to_sql
You may want to dip into Arel:
Model.select(Arel.star.count).to_sql
ASIDE:
I find I often want to find sub counts, so I embed the count(*) into another query:
child_counts = ChildModel.select(Arel.star.count)
.where(Model.arel_attribute(:id).eq(
ChildModel.arel_attribute(:model_id)))
Model.select(Arel.star).select(child_counts.as("child_count"))
.order(:id).limit(10).to_sql
which then gives you all the child counts for each of the models:
SELECT *,
(
SELECT COUNT(*)
FROM "child_models"
WHERE "models"."id" = "child_models"."model_id"
) child_count
FROM "models"
ORDER BY "models"."id" ASC
LIMIT 10
Best of luck
UPDATE:
Not sure if you are trying to solve this in a generic way or not. Also not sure what kind of scopes you are using on your Model.
We do have a method that automatically calls a count for a query that is put into the ui layer. I found using count(:all) is more stable than the simple count, but sounds like that does not overlap your use case. Maybe you can improve your solution using the except clause that we use:
scope.except(:select, :includes, :references, :offset, :limit, :order)
.count(:all)
The where clause and the joins necessary for the where clause work just fine for us. We tend to want to keep the joins and where clause since that needs to be part of the count. While you definitely want to remove the includes (which should be removed by rails automatically in my opinion), but the references (much trickier especially in the case where it references a has_many and requires a distinct) that starts to throw a wrench in there. If you need to use references, you may be able to convert these over to a left_join.
You may want to double check the parameters that these "join" methods take. Some of them take table names and others take relation names. Later rails version have gotten better and take relation names - be sure you are looking at the docs for the right version of rails.
Also, in our case, we spend more time trying to get sub selects with more complicated relationships, we have to do some munging. Looks like we are not dealing with where clauses as much.
ref2

How to shorten paths when accessing linked-server objects in SQL

Is there a way to shorted access paths in SQL? I am thinking something similar to alias's but I don't know how to ask this question in google to get the appropriate application of alias'ing
This:
select * from ServerName.DBName.dbo.TableName
To:
declare #RDB as RemoteDatabaseObject
set #RDB = ServerName.DBName.dbo
select * from #RDB.TableName
I know this doesnt work, but I want to know if there is a way to alias objects that have long paths.
Maybe you are looking for SYNONYM?
http://msdn.microsoft.com/en-us/library/ms177544.aspx
Aliasing works for what you're wanting here.
SELECT * FROM ServerName.DBName.dbo.TableName AS myAlias
The cool thing is that you can almost always use AS in the middle of sql statements.
http://www.w3schools.com/sql/sql_alias.asp
Other than using dynamic SQL, I dont think of any way which makes this possible.

For an Oracle NUMBER datatype, LIKE operator vs BETWEEN..AND operator

Assume mytable is an Oracle table and it has a field called id. The datatype of id is NUMBER(8). Compare the following queries:
select * from mytable where id like '715%'
and
select * from mytable where id between 71500000 and 71599999
I would think the second is more efficient since I think "number comparison" would require fewer number of assembly language instructions than "string comparison". I need a confirmation or correction. Please confirm/correct and throw any further comment related to either operator.
UPDATE: I forgot to mention 1 important piece of info. id in this case must be an 8-digit number.
If you only want values between 71500000 and 71599999 then yes the second one is much more efficient. The first one would also return values between 7150-7159, 71500-71599 etc. and so forth. You would either need to sift through unecessary results or write another couple lines of code to filter the rest of them out. The second option is definitely more efficient for what you seem to want to do.
It seems like the execution plan on the second query is more efficient.
The first query is doing a full table scan of the id's, whereas the second query is not.
My Test Data:
Execution Plan of first query:
Execution Plan of second query:
I don't like the idea of using LIKE with a numeric column.
Also, it may not give the results you are looking for.
If you have a value of 715000000, it will show up in the query result, even though it is larger than 71599999.
Also, I do not like between on principle.
If a thing is between two other things, it should not include those two other things. But this is just a personal annoyance.
I prefer to use >= and <= This avoids confusion when I read the query. In addition, sometimes I have to change the query to something like >= a and < c. If I started by using the between operator, I would have to rewrite it when I don't want to be inclusive.
Harv
In addition to the other points raised, using LIKE in the manner you suggest would cause Oracle to not use any indexes on the ID column due to the implicit conversion of the data from number to character, resulting in a full table scan when using LIKE versus and index range scan when using BETWEEN. Assuming, of course, you have an index on ID. Even if you don't, however, Oracle will have to do the type conversion on each value it scans in the LIKE case, which it won't have to do in the other.
You can use math function, otherwise you have to use to_char function to use like, but it will cause performance problems.
select * from mytable where floor(id /100000) = 715
or
select * from mytable where floor(id /100000) = TO_NUMBER('715') // this is parametric

How do I get all the columns of a table besides one

Suppose we have 20 columns in a table and I want to return 19 of them.
How can I do that ?
select *
will give me all of them but I want only 19.
Is there a good solution for that situation ? something like
select * - [columnName]
?!?
Nope, sorry. You can take *, or you can take them one at a time, but you can't take "all of them except for X, Y, or Z."
As has been said, you use SELECT * for all columns or list the columns individually if you don't want them all.
Listing columns does seem like a chore but there is an important reason why it's actually good.
While it's OK for ad hoc queries, it's highly recommended that use don't use SELECT * in code because when the database schema changes you will get different columns in the results returned to your application which is almost certainly not what you want. If you could do select * but address from customer this would have the same problem: changing the DB would change the structure of the results of your query which is bad.
So not only can you not do it, I would recommend not doing it even if you could.
You can explicitly name each column you wish to select. That is the only way to exclude columns.