SQL subquery within a SELECT calculation - sql

Very new to subqueries and find myself in need of help.
I would like to query from a single database. Within that query, I would like to calculate a variable from two variables with that database (SUBQ and TOTAL). My issue is this: my SUBQ variable needs to be subject to an additional set of WHERE constraints on top of those that will be employed for the whole query. Simplified example code below:
create table [blah]
as select date_part('YEAR',DATE) as Orig_Year,
sum([SUBQ variable])/sum(TOTAL) as UD_Rate
from [database]
where [full query requirements]
group by date_part('YEAR',DATE)
I have tried to create a subquery within that calculation by specifying a subquery in the FROM statement. So, for example,
select date_part('YEAR',DATE1) as Orig_year,
sum(a.SUBQ)/sum(b.TOTAL) as UD_Rate
from database b,
(select SUBQ
from database
where DATE2 is not null and
months_between(DATE3,DATE2) <= 100 and
VALUE1 in ('A','B')) a
where VALUE2 between 50.01 and 100
group by date_part('YEAR',DATE1)
Am I on the right track with my thinking here? I have yet to get anywhere close to a functional query and have had little luck finding a similar question online, so I'm at the point where I've tossed up my hands and come to you. Though I know little about them, would it be more appropriate to create a VIEW with the SUBQ value, and then merge it with the broader query?
Thoughts of pies and cakes for whoever is willing to assist me with this request. Thank you.

I think you just want condition aggregation in a window function. Something like this:
select sum(case when [subquery requirements] then t.subq else 0 end) / sum(t.Total)
from t;
I'm pretty sure this is what you are looking for. In terms of your create table:
select date_part('YEAR',DATE) as Orig_Year,
sum(case when ?? then Total else 0 end)/sum(TOTAL) as UD_Rate
from [database]
where [full query requirements]
group by date_part('YEAR', DATE);
I am guessing that the column to be compared is Total, subject to the conditions in the when.

Use Common-Table-Expression then:
-- Define the CTE expression name and column list.
WITH subquery (Orig_year, UD_Rate)
AS
-- Define the CTE query.
(
select date_part('YEAR',DATE1) as Orig_year,
sum(a.SUBQ)/sum(b.TOTAL) as UD_Rate
from database b,
(select SUBQ
from database
where DATE2 is not null and
months_between(DATE3,DATE2) <= 100 and
VALUE1 in ('A','B')) a
where VALUE2 between 50.01 and 100
group by date_part('YEAR',DATE1)
)
And then use subquery as you would use a table inside your main query

Related

Condition inside a count -SQL

I am trying to write a condition inside a count statement where it should only count the entries which do not have an ENDDATE. i am looking for writing the condition inside the count as this is a very small part of a large SQl Query
sample query,
select product, count(*) as quantity
from table
where end_date is null
group by age
This query lists quantity for each product which do not have an end date
One method uses conditional aggregation:
select sum(case when end_date is null then 1 else 0 end) as NumNull
. . .
Another method is just to subtract two counts:
select ( count(*) - count(end_date) ) as NumNull
count(end_date) counts the number that are not NULL, so subtracting this from the full count gets the number that are NULL.
Uhmmmm.
It sounds like you are looking for conditional aggregation.
So, if you have a current statement that's sort of working (and we're just guessing because we don't see anything you have attempted so far...)
SELECT COUNT(1)
FROM mytable t
And you want another another expression that returns a count of rows that meet some set of conditions...
and when you say "do not have an ENDDATE", you are refderring to rows that have an ENDDATE value of NULL (and again, we're just guessing that the table has a column named ENDDATE. Every row will have an ENDDATE column.)
We'll use a ANSI standards compliant CASE expression, because this would work in most databases (SQL Server, Oracle, MySQL, Postgres... and we don't have clue what database you are using.
SELECT COUNT(1)
, COUNT(CASE WHEN t.ENDDATE IS NULL THEN 1 ELSE NULL END) AS cnt_null_enddate
FROM mytable t

Is it possible to combine or reduce these sql statements?

I'm not particularly familiar with SQL, but my team asked me to take a look at this series of sql statements and see if it is possible to reduce it down to just 1 or 2. I looked at it, and I don't believe so, but I don't quite have the experience or knowledge of the tricks of sql.
So all of the statements have pretty much the same format
select
id, count(*)
from
table
where
x_date between to_date(start_date) and to_date(end_date)
group by
id
where x_date is the only thing that changes. (Start_date and end_date are just what I typed here to make it a bit more readable). There are 10 statements total, 7 of which are exactly this format.
Of the 3 different ones, one of them looks like this:
select
id, count(*)
from
table
where
x_date between to_date(start_date) and to_date(end_date)
and userid not like 'AUTOPEND'
group by
id
and the other 2 look like this:
select
id, count(*)
from
table
where
x_date between to_date(start_date) and to. _date(end_date)
group by
id, x_code
Where x_code differs between them.
They want to use this data for statistical analysis, but they insist on manually using a query and typing it in. The way I see it is that I can't really combine these statements because they are all grouping by the same field (except the last 2), so it all gets combined in the results, making the results useless for the analysis.
Am I thinking about it right, or is there some way to do like they asked? Can I make a 1 or 2 sql statements output more than 1 table each?
Oh, I almost forgot. I believe that this is Oracle PL/SQL using SQL developer.
You are trying to get multiple aggregates with different grouping sets in a single query. This is called a ROLLUP or a CUBE. There are a few ways to solve your specific problem, but the extended grouping functions are the right tool for the job. Going forward, it will be more maintainable and faster.
http://www.oracle-base.com/articles/misc/rollup-cube-grouping-functions-and-grouping-sets.php
Since the first and second example are grouping by the same thing you can use CASE statement nested in an aggregation function:
SELECT id, COUNT(*) ,
SUM( CASE WHEN userid not like 'AUTOPEND' THEN 1 ELSE 0 END) AS [NotAUTOPEND]
FROM table
WHERE x_date between to_date(start_date) and to_date(end_date)
GROUP BY id

Average when meeting where clause

I want to find the average amount of a field where it meets a criterion. It is embedded in a big table but I would like this average field in there instead of doing it in a separate table.
This is what I have so far:
Select....
Avg( (currbal) where (select * from table
where ament2 in ('r1','r2'))
From table
If you want to AVG only a subset of a query use case when ... then to replace value in non-matching rows with null as nulls are ignored by avg().
Select id,
sum(something) SomethingSummed,
avg(case when ament2 in ('r1','r2') then currbal end) CurrbalAveragedForR1R2
From [table]
group by id
You can put all the other sums which you want to be embedded into the AVG statement, inside the table reference inside the FROM clause. Something like:
SELECT AVG(currbal)
FROM
(
SELECT * -- other sums
FROM table
WHERE ament2 IN ('r1','r2')
) t
You can write a full sub-select into the select list:
SELECT ...,
(SELECT AVG(Currbal) FROM Table WHERE ament2 IN ('r1', 'r2')) AS avg_currbal,
...
FROM ...
Whether that will do exactly what you want depends on a number of things. You might need to make that into a correlated subquery; assuming 'ament2' is in Table, it is not a correlated sub-query at the moment.

TOP function in SQL Server 2005 not giving correct answer

How can I use TOP function in SQL Server 2005 on a single column in a table along with count function?
I am getting only one count for this query, where I have 35 entries that should come.
this is my query
select top(1) room_no, count(room_no) from rooms
Seems like what you want is the following:
select room_no,count(room_no)
from rooms
group by room_no
BTW, I wonder why it would execute without the group by. Should throw an error.
COUNT function is evaluated after TOP. That is why you are only getting one for the count. What you want is something like this
SELECT TOP(1) dbo.Table.Column1, (SELECT COUNT(*) FROM dbo.Table)
FROM dbo.Table
Also as mentioned you really should be ordering by something.
Edit: This also works:
SELECT TOP(1) dbo.Table.Column1, COUNT(*) OVER() AS Total
FROM dbo.Table
Seems to be more efficient as well (only tested on small dataset though).

SQL query trick

I need to do
select * from xxx where name in (a,b,c...);
but I want the result set to be in the order of (a,b,c...). is this possible?
I found this question which is looks like your original question: Ordering by the order of values in a SQL IN() clause
ah - I see. you could do something horrendous with a case statement, and then order by that.. you'd effectivley be adding another column to your query to be an "order" that you could then "order by"
its ugly, but if you control the query, and the number in the 'in' clause is low, it could work (beleive an 'in' clause is limited to 255 chars)
e.g "IF name = a then 1 else if name = b then 2"
Failing that, probably best to sort in the client using a similar technique (assuming it was the client that injected the information into the 'in' clause in the first place)
-Ace
The method to do this will be DB-specific.
In Oracle, you could do something like:
SELECT * FROM xxx
where name in (a,b,c...)
ORDER BY DECODE(name,a,1,b,2,c,3);
IN statements are pretty limited, but you could get a similar effect by joining on a subquery.
here's an example:
SELECT x.*
FROM xxx as x
INNER JOIN ((select a as name, 1 as ord)
UNION
(select b as name, 2 as ord)
UNION
(select c as name, 3 as ord)) as t
ON t.name = x.name
ORDER BY t.ord
its pretty ugly, but it should work on just about any sql database. The ord field explicitly allows you to set the ordering of the result. some databases such as SqlServer support a ROWINDEX feature so you may be able to use that to clean it up a bit.