SQL query sum at bottom row - sql

I am trying to get the sum of a column at the bottom row.
I have tried a few examples by using SUM() and COUNT(), but they have all failed with syntax errors.
Here is my current code without any sum or anything:
:XML ON
USE MYTABLE
SELECT sbc.PolicyC.PolicyName as namn,COUNT(*) as cnt
FROM sbc.AgentC, sbc.PolicyC
WHERE sbc.AgentC.PolicyGuid = sbc.PolicyC.PolicyGuid
GROUP BY sbc.AgentC.PolicyGuid, sbc.PolicyC.PolicyName ORDER BY namn ASC
FOR XML PATH ('celler'), ROOT('root')
GO
The XML output is reformatted to become a regular HTML table.
EDIT:
Here is the latest code, but it generates a "sum" (same number as the row above) on every other row:
:XML ON
USE MYTABLE
SELECT sbc.PolicyC.PolicyName as namn,COUNT(*) as cnt
FROM sbc.AgentC, sbc.PolicyC
WHERE sbc.AgentC.PolicyGuid = sbc.PolicyC.PolicyGuid
GROUP BY sbc.AgentC.PolicyGuid, sbc.PolicyC.PolicyName with rollup
FOR XML PATH ('celler'), ROOT('root')
GO
The XML output looks like this:
<root>
<celler>
<namn>example name one</namn>
<cnt>23</cnt>
</celler>
<celler>
<cnt>23</cnt>
</celler>
<celler>
<namn>example name two</namn>
<cnt>1</cnt>
</celler>
<celler>
<cnt>1</cnt>
</celler>
</root>

Try
SELECT sbc.PolicyC.PolicyName as namn,COUNT(*) as cnt
FROM sbc.AgentC, sbc.PolicyC
WHERE sbc.AgentC.PolicyGuid = sbc.PolicyC.PolicyGuid
GROUP BY sbc.AgentC.PolicyGuid, sbc.PolicyC.PolicyName
UNION
SELECT 'TOTAL' as nawn,COUNT(*) as cnt
FROM
FROM sbc.AgentC, sbc.PolicyC
WHERE sbc.AgentC.PolicyGuid = sbc.PolicyC.PolicyGuid
ORDER BY namn ASC
This will compute the total in a separate query. However, you might need to either add some non-printing, high-ASCII character to force the total to the bottom, or add some numeric ordering key... mySQL may also have an operator (similar to WITH ROLLUP in Microsoft SQL) which would be more efficient than the above code... So while this would work, there are probably more efficient options available to you...

MySQL supports a rollup extension to group by.
select * from parts;
+-----------+--------+
| part_name | amount |
+-----------+--------+
| upper | 100 |
| lower | 100 |
| left | 50 |
| right | 50 |
+-----------+--------+
select part_name
,sum(amount)
from parts
group
by part_name with rollup;
+-----------+-------------+
| part_name | sum(amount) |
+-----------+-------------+
| left | 50 |
| lower | 100 |
| right | 50 |
| upper | 100 |
| NULL | 300 |
+-----------+-------------+
Updated to answer comments:
The following items list some
behaviors specific to the MySQL
implementation of ROLLUP:
When you use ROLLUP, you cannot also
use an ORDER BY clause to sort the
results. In other words, ROLLUP and
ORDER BY are mutually exclusive.
However, you still have some control
over sort order. GROUP BY in MySQL
sorts results, and you can use
explicit ASC and DESC keywords with
columns named in the GROUP BY list to
specify sort order for individual
columns. (The higher-level summary
rows added by ROLLUP still appear
after the rows from which they are
calculated, regardless of the sort
order.)

My code became somthing like:
SELECT * FROM (...old code here... UNION ...'Total:' ... COUNT() ...)* z
ORDER BY CASE WHEN z.Namn = 'Total:' THEN '2' ELSE '1' END , z.Antal DESC
I have one column named Namn and one named Antal.
If there is the value 'Total:' in the column Namn it will order that as a '2' and if not as a '1', that makes the 'Total:' move to the botton when I have decendent ordering on the Antal column.
The magic hapens because the 'Total:' is UNION with the table, and then the CASE statement at the end puts it at the end.
My complete code that works for me that is a loot moore messy, it unions 2 tables and stuff as well:
SELECT * FROM (
SELECT acrclient.Client_Name AS 'Namn', COUNT(x.client) AS 'Antal'
FROM
(SELECT 'B' tab,t.client
FROM asutrans t
where t.voucher_type!='IP' AND t.last_update >= {ts '2019-01-01 00:00:00'}
UNION ALL SELECT
'C' tab,t.client
FROM asuhistr t
WHERE t.voucher_type!='IP' AND t.last_update >= {ts '2019-01-01 00:00:00'} ) x
LEFT JOIN acrclient ON x.client = acrclient.client
GROUP BY x.client, acrclient.Client_Name
UNION ALL
SELECT 'Total:', COUNT(client) FROM (SELECT 'B' tab,t.client
FROM asutrans t
where t.voucher_type!='IP' AND t.last_update >= {ts '2019-01-01 00:00:00'}
UNION ALL SELECT
'C' tab,t.client
FROM asuhistr t
WHERE t.voucher_type!='IP' AND t.last_update >= {ts '2019-01-01 00:00:00'} ) y
) z
ORDER BY CASE WHEN z.Namn = 'Total:' THEN '2' ELSE '1' END , z.Antal DESC

Related

postgresql total column sum

SELECT
SELECT pp.id, TO_CHAR(pp.created_dt::date, 'dd.mm.yyyy') AS "Date", CAST(pp.created_dt AS time(0)) AS "Time",
au.username AS "User", ss.name AS "Service", pp.amount, REPLACE(pp.status, 'SUCCESS', ' ') AS "Status",
pp.account AS "Props", pp.external_id AS "External", COALESCE(pp.external_status, null, 'indefined') AS "External status"
FROM payment AS pp
INNER JOIN auth_user AS au ON au.id = pp.creator_id
INNER JOIN services_service AS ss ON ss.id = pp.service_id
WHERE pp.created_dt::date = (CURRENT_DATE - INTERVAL '1' day)::date
AND ss.name = 'Some Name' AND pp.status = 'SUCCESS'
id | Date | Time | Service |amount | Status |
------+-----------+-----------+------------+-------+--------+---
9 | 2021.11.1 | 12:20:01 | some serv | 100 | stat |
10 | 2021.12.1 | 12:20:01 | some serv | 89 | stat |
------+-----------+-----------+------------+-------+--------+-----
Total | | | | 189 | |
I have a SELECT like this. I need to get something like the one shown above. That is, I need to get the total of one column. I've tried a lot of things already, but nothing works out for me.
If I understand correctly you want a result where extra row with aggregated value is appended after result of original query. You can achieve it multiple ways:
1. (recommended) the simplest way is probably to union your original query with helper query:
with t(id,other_column1,other_column2,amount) as (values
(9,'some serv','stat',100),
(10,'some serv','stat',89)
)
select t.id::text, t.other_column1, t.other_column2, t.amount from t
union all
select 'Total', null, null, sum(amount) from t
2. you can also use group by rollup clause whose purpose is exactly this. Your case makes it harder since your query contains many columns uninvolved in aggregation. Hence it is better to compute aggregation aside and join unimportant data later:
with t(id,other_column1,other_column2,amount) as (values
(9,'some serv','stat',100),
(10,'some serv','stat',89)
)
select case when t.id is null then 'Total' else t.id::text end as id
, t.other_column1
, t.other_column2
, case when t.id is null then ext.sum else t.amount end as amount
from (
select t.id, sum(amount) as sum
from t
group by rollup(t.id)
) ext
left join t on ext.id = t.id
order by ext.id
3. For completeness I just show you what should be done to avoid join. In that case group by clause would have to use all columns except amount (to preserve original rows) plus the aggregation (to get the sum row) hence the grouping sets clause with 2 sets is handy. (The rollup clause is special case of grouping sets after all.) The obvious drawback is repeating case grouping... expression for each column uninvolved in aggregation.
with t(id,other_column1,other_column2,amount) as (values
(9,'some serv','stat',100),
(10,'some serv2','stat',89)
)
select case grouping(t.id) when 0 then t.id::text else 'Total' end as id
, case grouping(t.id) when 0 then t.other_column1 end as other_column1
, case grouping(t.id) when 0 then t.other_column2 end as other_column2
, sum(t.amount) as amount
from t
group by grouping sets((t.id, t.other_column1, t.other_column2), ())
order by t.id
See example (db fiddle):
(To be frank, I can hardly imagine any purpose other than plain reporting where a column mixes id of number type with label Total of text type.)

How to filter out conditions based on a group by in JPA?

I have a table like
| customer | profile | status | date |
| 1 | 1 | DONE | mmddyy |
| 1 | 1 | DONE | mmddyy |
In this case, I want to group by on the profile ID having max date. Profiles can be repeated. I've ruled out Java 8 streams as I have many conditions here.
I want to convert the following SQL into JPQL:
select customer, profile, status, max(date)
from tbl
group by profile, customer,status, date, column-k
having count(profile)>0 and status='DONE';
Can someone tell how can I write this query in JPQL if it is correct in SQL? If I declare columns in select it is needed in group by as well and the query results are different.
I am guessing that you want the most recent customer/profile combination that is done.
If so, the correct SQL is:
select t.*
from t
where t.date = (select max(t2.date)
from t t2
where t2.customer = t.customer and t2.profile = t.profile
) and
t.status = 'DONE';
I don't know how to convert this to JPQL, but you might as well start with working SQL code.
In your query date column not needed in group by and status='DONE' should be added with where clause
select customer, profile, status, max(date)
from tbl
where status='DONE'
group by profile, customer,status,
having count(profile)>0

Teradata/SQL sum together columns

I have a query that returns two amounts. I would like to sum them together, while leaving one as it is returned by the query.
For example:
DESC | sum(AMOUNT)
A | -61149025.940000
B | -9696.910000
B needs to be the sum of B and A and replace the current value. A stays as is on the result of the query.
My SQL is similar to this:
Select SQ.DESC, SUM(SQ.AMOUNT)
FROM (subquery) SQ
GROUP by SQ.DESC
My return results would be
DESC | sum(AMOUNT)
A | -61149025.940000
B | -61158722.850000
I have not been able to logically make sense of this
If you want "B" to be the total of all the values, you can use window functions:
Select SQ.DESC,
(CASE WHEN SQ.DESC = 'A' THEN SUM(SQ.AMOUNT)
WHEN SQ.DESC = 'B' THEN SUM(SUM(SQ.AMOUNT)) OVER ()
END) as SUM_AMOUNT
FROM (subquery) SQ
GROUP by SQ.DESC
Note: Even if Teradata allows DESC as a column name, it is a poor choice because DESC is a SQL keyword (think ORDER BY).

Getting rows with the highest SELECT COUNT from groups within a resultset

I have a SQLite Database that contains parsed Apache log lines.
A simplified version of the DB's only table (accesses) looks like this:
|referrer|datestamp|
+--------+---------+
|xy.de | 20170414|
|ab.at | 20170414|
|xy.de | 20170414|
|xy.de | 20170414|
|12.com | 20170413|
|12.com | 20170413|
|xy.de | 20170413|
|12.com | 20170413|
|12.com | 20170412|
|xy.de | 20170412|
|12.com | 20170412|
|12.com | 20170412|
|ab.at | 20170412|
|ab.at | 20170412|
|12.com | 20170412|
+--------+---------+
I am trying to retrieve the top referrer for each day by performing a sub query that does a SELECT COUNT on the referrer. Afterwards I select the entries from that subquery that have the highest count:
SELECT datestamp, referrer, COUNT(*)
FROM accesses WHERE datestamp BETWEEN '20170414' AND '20170414'
GROUP BY referrer
HAVING COUNT(*) = (select MAX(anz)
FROM (SELECT COUNT(*) anz
FROM accesses
WHERE datestamp BETWEEN '20170414' AND '20170414'
GROUP BY referrer
)
);
The above approach works as long as I perform the query for a single date, but it falls apart as soon as I query for date ranges.
How can I achieve grouping by date? I am also only interested in the referrer with the highest count.
If you want all the days combined with a single best referrer, then:
SELECT referrer, COUNT(*) as anz
FROM accesses
WHERE datestamp BETWEEN '20170414' AND '20170414'
GROUP BY referrer
ORDER BY COUNT(*) DESC
LIMIT 1;
I think you might want this information broken out by day. If so, a correlated subquery helps -- and a CTE as well:
WITH dr as (
SELECT a.datestamp, a.referrer, COUNT(*) as cnt
FROM accesses a
WHERE datestamp BETWEEN '20170414' AND '20170414'
GROUP BY a.referrer, a.datestamp
)
SELECT dr.*
FROM dr
WHERE dr.cnt = (SELECT MAX(dr2.cnt)
FROM dr dr2
WHERE dr2.datestamp = dr.datestamp
);
Just group by a date range. As an example,
SELECT referrer,
case when datestamp Between '20170101' AND '20170131' then 1
when datestamp Between '20170201' AND '20170228' then 2
when datestamp Between '20170301' AND '20170331' then 3
else 4 end DateRange
COUNT(*) as anz
FROM accesses
GROUP BY referrer,
case when datestamp Between '20170101' AND '20170131' then 1
when datestamp Between '20170201' AND '20170228' then 2
when datestamp Between '20170301' AND '20170331' then 3
else 4 end
ORDER BY referrer, COUNT(*) DESC
LIMIT 1;
You can put any legal SQL expression in a group by clause. This causes the Query processor to create individual buckets to aggregate the raw data into according to value of the group by expression.

Selecting records from subquery found set (postgres)

I have a query on 2 tables (part, price). The simplified version of this query is:
SELECT price.*
FROM price
INNER JOIN parts ON (price.code = part.code )
WHERE price.type = '01'
ORDER BY date DESC
That returns several records:
code | type | date | price | file
-------------+----------+------------------------------------------------------
00065064705 | 01 | 2008-01-07 00:00:00 | 16.400000 | 28SEP2011.zip
00065064705 | 01 | 2007-02-05 00:00:00 | 15.200000 | 20JUL2011.zip
54868278900 | 01 | 2006-02-24 00:00:00 | 16.642000 | 28SEP2011.zip
As you can see, there is code 00065064705 listed twice. I just need the maxdate record (2008-01-07) along with the code, type, date and price for each unique code. So basically the top record for each unique code. This postgres so I can't use SELECT TOP or something like that.
I think I should be using this as subquery inside of a main query but I'm not sure how. something like
SELECT *
FROM price
JOIN (insert my original query here) AS price2 ON price.code = price2.code
Any help would be greatly appreciated.
You can use the row_number() window function to do that.
select *
from (SELECT price.*,
row_number() over (partition by price.code order by price.date desc) as rn
FROM price
INNER JOIN parts ON (price.code = part.code )
WHERE price.type='01') x
where rn = 1
ORDER BY date DESC
(*) Note: I may have prefixed some of the columns incorrectly, as I'm not sure which column is in which table. I'm sure you can fix that.
In Postgres you can use DISTINCT ON:
SELECT DISTINCT ON(code) *
FROM price
INNER JOIN parts ON price.code = part.code
WHERE price.type='01'
ORDER BY code, "date" DESC
select distinct on (code)
code, p.type, p.date, p.price, p.file
from
price p
inner join
parts using (code)
where p.type='01'
order by code, p.date desc
http://www.postgresql.org/docs/current/static/sql-select.html#SQL-DISTINCT