Simple SQL select sum and values of same column - sql

I have a co-worker who is working on a table with an 'amount' column.
They would like to get the top 5 amounts and the sum of the amounts in the same query.
I know you could do this:
SELECT TOP 5 amount FROM table
UNION SELECT SUM(amount) FROM table
ORDER BY amount DESC
But this produces results like this:
1000 (sum)
100
70
50
30
20
When what they really need is this:
100 | 1000
70 | 1000
50 | 1000
30 | 1000
20 | 1000
My intuitive attempts to achieve this tend to run into grouping problems, which isn't such an issue when you are selecting a different column, but is when you want to use an aggregate function based on the column you are selecting.

You can use a CROSS JOIN for this:
SELECT TOP 5 a.amount, b.sum
FROM table a
CROSS JOIN (SELECT SUM(amount) sum FROM table) b
ORDER BY amount DESC

This might work
SELECT TOP 5 amount, (SELECT SUM(amount) FROM table)
FROM table
ORDER BY amount DESC

Not really pretty, but this shouls do it:
SELECT TOP 5 amount, SAmount
FROM table Join
(SELECT SUM(amount) As SAmount FROM table)
ORDER BY amount DESC
As said by others, I'd probably use two queries.

Another approach using analytic functions (SQL Server 2005+):
SELECT TOP 5 amount, SUM(amount) OVER()
FROM table
ORDER BY
amount DESC

Related

PostgreSQL using sum in where clause

I have a table which has a numeric column named 'capacity'. I want to select first rows which the total sum of their capacity is no greater than X, Sth like this query
select * from table where sum(capacity )<X
But I know I can not use aggregation functions in where part.So what other ways exists for this problem?
Here is some sample data
id| capacity
1 | 12
2 | 13.5
3 | 15
I want to list rows which their sum is less than 26 with the order of id, so a query like this
select * from table where sum(capacity )<26 order by id
and it must give me
id| capacity
1 | 12
2 | 13.5
because 12+13.5<26
A bit late to the party, but for future reference, the following should work for a similar problem as the OP's:
SELECT id, sum(capacity)
FROM table
GROUP BY id
HAVING sum(capacity) < 26
ORDER by id ASC;
Use the PostgreSQL docs for reference to aggregate functions: https://www.postgresql.org/docs/9.1/tutorial-agg.html
Use Having clause
select * from table order by id having sum(capacity)<X
You can use the window variant of sum to produce a cumulative sum, and then use it in the where clause. Note that window functions can't be placed directly in the where clause, so you'd need a subquery:
SELECT id, capacity
FROM (SELECT id, capacity, SUM(capacity) OVER (ORDER BY id ASC) AS cum_sum
FROM mytable) t
WHERE cum_sum < 26
ORDER BY id ASC;

Is it possible to create and use window function in the same query?

I'm using PostgreSQL and I have the following situation:
table of Sales (short version):
itemid quantity
5 10
5 12
6 1
table of stock (short version):
itemid stock
5 30
6 1
I have a complex query that also needs to present in one of it's columns the SUM of each itemid.
So it's going to be:
Select other things,itemid,stock, SUM (quantity) OVER (PARTITION BY itemid) AS total_sales
from .....
sales
stock
This query is OK. however this query will present:
itemid stock total_sales
5 30 22
6 1 1
But I don't need to see itemid=6 because the whole stock was sold. meaning that I need a WHERE condition like:
WHERE total_sales<stock
but I can't do that as the total_sales is created after the WHERE is done.
Is there a way to solve this without surrounding the whole query with another one? I'm trying to avoid it if I can.
You can use a subquery or CTE:
select s.*
from (Select other things,itemid,stock,
SUM(quantity) OVER (PARTITION BY itemid) AS total_sales
from .....
) s
where total_sales < stock;
You cannot use table aliases defined in a SELECT in the SELECT, WHERE, or FROM clauses for that SELECT. However, a subquery or CTE gets around this restriction.
You can also use an inner select in your WHERE statement like this:
SELECT *, SUM (quantity) OVER (PARTITION BY itemid) AS total_sales
FROM t
WHERE quantity <> (SELECT SUM(quantity) FROM t ti WHERE t.itemid = ti.itemid);

Wired sql query

sqlite> select sid,max(amount) from(select * from Am where year=2014);
1,600
sqlite> select sid from (select sid,max(amount) from(select * from Am where year=2014));
3
Where 3 come from?! the only option is 1..
Whats going on?
Thanks.
select sid, max(amount) doesn't seem very well-defined. I think you're assuming it will give you the sid for the row that has the max amount, I don't think that is a valid assumption.
I would write this as
select sid from Am where year = 2014
and amount = (select max(amount) from Am where year = 2014)
which can return multiple rows if there's a tie for the maximum amount.
Now you know what indeterminate means,if you don`t aggregate you need to group by, other databases dont even allow this operation.
Suppose you have these values:
sid amount
1 600
1 500
3 400
3 200
select sid,max(amount) from t
Will return an indeterminate value from sid and 600 for MAX.Sid might be correct or might not,that`s what indeterminate means,because you don`t have any control on that column.A proper query would be
select sid,max(amount) from t where year = 2014 GROUP BY sid
Or
select max(amount) from t

Column Sum in Select Query

I have problem in sql server select query. I have following table.
ID-----Name----Quantity
1-------a-----------10
2-------b-----------30
3-------c-----------20
4-------d-----------15
5-------e-----------10
6-------f-----------30
7-------g-----------40
I want to select those record where the sum of Quantity < value. For example if I say select those record where the Quantity sum <65 then the output will be
ID-----Name----Quantity
1-------a-----------10
2-------b-----------30
3-------c-----------20
because if we include the next record then the sum of Quantity will 75.
I want to create this query. Please help me out.
You can simply use a correlated subquery to do so, and it will work fine for both MySQL, and SQL Server. But it is not the best performance wise solution:
SELECT
ID,
Name,
Quantity
FROM
(
SELECT
t1.ID,
t1.Name,
t1.Quantity,
(SELECT SUM(t2.Quantity)
FROM tablename AS t2
WHERE t2.ID <= t1.ID) AS Total
FROM Tablename AS t1
) AS t
WHERE Total < 65;
See it in action:
SQL Fiddle Demo
This will give you:
| ID | NAME | QUANTITY |
------------------------
| 1 | a | 10 |
| 2 | b | 30 |
| 3 | c | 20 |
Best performance wise solution is using recursive CTE.
WITH CTE_Prepare AS
(
SELECT
*,
ROW_NUMBER() OVER (ORDER BY id) AS RN
FROM TableRT
)
, CTE_Recursive AS
(
SELECT ID, Name, Quantity, QUantity AS SumQuantity, RN FROM CTE_Prepare WHERE RN = 1
UNION ALL
SELECT p.ID, p.Name, p.Quantity, r.SumQuantity + p.Quantity AS SumQuantity, r.RN + 1 AS RN FROM CTE_Recursive r
INNER JOIN CTE_Prepare p ON p.RN = r.RN+1
WHERE r.SumQuantity + p.Quantity < 65
)
SELECT *
FROM CTE_Recursive
OPTION (MAXRECURSION 0)
The first CTE is just to calculate ROW_NUMBERS to use instead of your IDs, because it's important not to have gaps and we can't usually be sure with any ID that it would be the case.
Second CTE is two-part recursive, adding Quantity for each next row. You can google about SQL Server recursive CTEs more if needed.
I think this is better then any other approach to find running totals (that's what this concept is called) because it only works with two rows at the time - not adding all previous rows for each calculation and it actually stops as soon it reaches the wanted mark.
SQLFiddle - few rows sample
SQLFiddle - 10000 rows
EDIT: Corrected a few mistakes. In order for this to be fast, WHERE clause needs to be inside CTE and not outside.

How to return total number of records with TOP * select

I have over 300 000 rows, I would like to return total number of the records even if I pick only 20/50/100 records using TOP * statement.
Is it possible to filter select like
select top 50 * from table where rule1=rule1 and rule=rule2
Let's say that total number of records if database is 300 000 and select above would return 4 000 records. But physically it will return only 50 records, I need to know how manu is there in database using this where statement (it would return 4000).
Thanks
select top 50 *, count(*) over()
from table
where rule1=rule1 and rule=rule2
There are a lot of simple answers to this question, as other posters have pointed out. There is also a lot of subtlety depending on your scenario. There is a fairly in depth discussion of the issue # Efficient way of getting ##rowcount from a query using row_number
SELECT TOP 50
*
FROM TableName t1
INNER JOIN (SELECT
COUNT(*) AS CountOfRecords
FROM TableName) t2 on 1=1
You could do:
select top 50 *, (select count(*) from table)
from table
where rule1=rule1 and rule=rule2
This will give you the total number of rows as an extra column against each row returned by the main query. Not sure of the performance implications on this though...
Wasn't clear from your question if you need the count to be based on the filtered number of rows or not, but if so:
select top 50 *, (select count(*) from table where rule1=rule1 and rule=rule2)
from table
where rule1=rule1 and rule=rule2