SQL: Computing sum of all values *and* a sum of only values matching condition - sql

Suppose I fetch a set of rows from several tables. I want to know the total sum of values in column x in these rows, as well as sum of only those values in x where the row satisfies some additional condition.
For example, let's say I fetched:
X Y
1 0
10 0
20 100
35 100
I want to have a sum of all x (66) and x in those rows where x > y (11). So, I'd need something like:
SELECT sum(x) all_x, sum(x /* but only where x > y */) some_x FROM ...
Is there a way to formulate that in SQL? (Note that the condition is not a separate column in some table, I cannot group over it, or at least don't know how to do that.)
EDIT: I use Oracle Database, so depending on Oracle extensions is OK.

You could use a case expression inside the sum:
SELECT SUM (x) all_x,
SUM (CASE WHEN x > y THEN x ELSE 0 END) some_x
FROM my_table

You're looking for the CASE operator :
SELECT sum(X) all_x,
sum(CASE WHEN X>Y THEN X ELSE 0 END) some_x
FROM Table1
In this case (no pun) you would get 11 for some_x
You can use whatever condition you want instead of X>Y after the WHEN, and select whatever value instead of X.
SQL fiddle to test this query.

Below Query will give What you want
select SUM(x) as x,(select SUM(x) from test5 where x>y )as 'X>Y'
from test5

Related

SQL calculate percentage from calculated column

I have a table with multiple columns however I need to calculate a Total Percentage based off 2 columns.
Column 1 has unique identifier (number i.e. 15211, 36521, 45987 etc)
Column 2 has a "Y" or is blank (the criteria is built in to the DWH)
What i am wanting to do is get a Percentage of Column 2 of only the Y fields using Column 1 as the Denominator
Column 1
Column 2
25638
y
69857
n
78561
n
23149
y
based on the example above im expecting 2/4 = 0.50 or 50%
You can divide the result of a conditional aggregation on Column2 = 'Y', and the overall count.
SELECT COUNT(CASE WHEN Column2 = 'y' THEN 1 END) / COUNT(*) AS perc_y
FROM tab
Output:
perc_y
0.5000
If you want a percentage, multiply by 100, round up and concatenate with '%'.
Here's a demo in MySQL, although it should work on all the most common DBMS'.

For each value in col A finding number of values in column B that are greater than it

Let's say I have a table with 2 columns - A & B.
Using plain SQL (No scripts/cursors etc.), how do I (window function?) calculate for EACH value in column A the number of values in column B that are bigger/smaller than it?
Thanks you.
You would use conditional aggregation:
select a,
sum(case when b < a then 1 else 0 end)
from t
group by a;
Window functions don't seem appropriate to this question.

Postgres union of queries in loop

I have a table with two columns. Let's call them
array_column and text_column
I'm trying to write a query to find out, for K ranging from 1 to 10, in how many rows does the value in text_column appear in the first K elements of array_column
I'm expecting results like:
k | count
________________
1 | 70
2 | 85
3 | 90
...
I did manage to get these results by simply repeating the query 10 times and uniting the results, which looks like this:
SELECT 1 AS k, count(*) FROM table WHERE array_column[1:1] #> ARRAY[text_column]
UNION ALL
SELECT 2 AS k, count(*) FROM table WHERE array_column[1:2] #> ARRAY[text_column]
UNION ALL
SELECT 3 AS k, count(*) FROM table WHERE array_column[1:3] #> ARRAY[text_column]
...
But that doesn't looks like the correct way to do it. What if I wanted a very large range for K?
So my question is, is it possible to perform queries in a loop, and unite the results from each query? Or, if this is not the correct approach to the problem, how would you do it?
Thanks in advance!
You could use array_positions() which returns an array of all positions where the argument was found in the array, e.g.
select t.*,
array_positions(array_column, text_column)
from the_table t;
This returns a different result but is a lot more efficient as you don't need to increase the overall size of the result. To only consider the first ten array elements, just pass a slice to the function:
select t.*,
array_positions(array_column[1:10], text_column)
from the_table t;
To limit the result to only rows that actually contain the value you can use:
select t.*,
array_positions(array_column[1:10], text_column)
from the_table t
where text_column = any(array_column[1:10]);
To get your desired result, you could use unnest() to turn that into rows:
select k, count(*)
from the_table t, unnest(array_positions(array_column[1:10], text_column)) as k
where text_column = any(array_column[1:10])
group by k
order by k;
You can use the generate_series function to generate a table with the expected number of rows with the expected values and then join to it within the query, like so:
SELECT t.k AS k, count(*)
FROM table
--right join ensures that you will get a value of 0 if there are no records meeting the criteria
right join (select generate_series(1,10) as k) t
on array_column[1:t.k] #> ARRAY[text_column]
group by t.k
This is probably the closest thing to using a loop to go through the results without using something like PL/SQL to do an actual loop in a user-defined function.

How to divide columns with zeros and nulls

just a simple question but somehow I can't find an answer here.
I have two columns (A and B). Both contains numbers with zeros and null. I would like to get a division one by the other to get information about the ratio between each single row but I am getting ORA-01476.
I know the divisior is equal to zero but I would like to get in this row a number and not an error for whole query
A B
1 5
2 Null
3 0
NULL 3
0 4
4
I am using sql developer.
If you divide a number by zero you get an error, because the answer to such division is undefined. SQL, however, has a value for undefined: NULL. So make the result NULLinstead:
select a, b, case when b = 0 then null else a / b end as ratio
from mytable;
or
select a, b, a / case when b = 0 then null else b end as ratio
from mytable;
This is standard SQL and works in Oracle as well as in about every other RDBMS. Oracle also provides the function NULLIF as a shorter way to write the expression in the second query.
You can use nullif to return null instead of raising an error:
select A / nullif(B, 0) as division
from YourTable
If your numbers are stored as varchar, cast them to numbers before using them:
select to_number(A) / nullif(to_number(B), 0) as division
from YourTable

SQL to return one row for each distinct value of a column (do not mind which row)

I have a table with a column named X. X contains number from 0 to 99. But there are duplicates (e.g. 0 is there multiple times! )
Now I need a query that gives any of the rows with 0,1,2,3...99 meaning I get 100 results at with one query, but I don't care which of the x==0 , x==1 ... I get, but just one of them!
Is there such thing in sql?
select distinct x
from your_table
To get a complete record you can group by the X column. But you have to tell the DB which of the duplicate values of the other columns you want.
select x, min(y) as y
from your_table
group by x
If you build a group by X then this value will be distinct. For the other columns you need a so called aggregate function like for example min(). That tells the DB to pick the minimum Y of every X group.