Teradata/SQL sum together columns - sql

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).

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.)

SQL: Checking value counts of a column

I'd like to check if a column in a table has values with a small number of value counts.
Consider the following table as an example:
RowID |Product
1 | A
2 | A
3 | B
...
200.000 | C
the following table is aggregated of the table above:
Product |Count
A |204
B |682
C |553
D |1402
E |30855
F |357
G |1
H |542
What I'd like to know of the column Product of my table is, whether or not a Product has a count that is less than 5%. And if so, the SQL statement should return: 'Some values of this field have a small number of value counts'
In other words: IF [MinValueCount]/[Count] <= .05 then 'Some values of this field have a small number of value counts' else 'null'
With the example above, I should get: 'Some values of this field have a small number of value counts'
as product G is less than 5% of the total count of products.
how should the SQL statement look like?
With kind regards,
Lazzanova
Use two levels of aggregation. You can get the total using window functions:
select max( 'Some values of this field have a small number of value counts')
from (select product, count(*) as cnt,
sum(count(*)) over () as total_cnt
from t
) t
where cnt < 0.05 * total_cnt;
The use of max() in the outer query is just to return one row. You could also use fetch or a similar clause (whatever your database supports):
select 'Some values of this field have a small number of value counts'
from (select product, count(*) as cnt,
sum(count(*)) over () as total_cnt
from t
) t
where cnt < 0.05 * total_cnt
fetch first 1 row only;

Sort by one column, but get offset by another

Let's say I have a table with two columns:
| ID | A |
I want to sort by A, then get the 10 records after a given ID. What would be the best way to handle this in Postgres?
To clarify, I want to sort by A, but do my pagination by the ID. So if I had a table like:
1 | 'C'
2 | 'B'
3 | 'A'
4 | 'G'
5 | 'A'
6 | 'H'
So after sorting by A, I'd want the first three values after id=1, so:
1 | 'C'
4 | 'G'
6 | 'H'
An ordering of any column is purely dependant on the order by clause and the terms "before" or "after" come into picture only when there's a pre-determined order. So, once the records are ordered by column "A", there's no guarantee that the id's will be ordered in the sequence 1,4,6, unless you also specified that ordering of id.
So, if you
want the first three values after id=1
It means there should be a way to determine the point where the id value has become 1 and all the rows beyond are to be considered. To ensure that you have to explicitly include id in the order by. A COUNT analytic function can come to our rescue to mark the point.
SELECT id,a
FROM ( SELECT t.*,COUNT(CASE WHEN id = 1 THEN 1 END) --the :id argument
OVER( ORDER BY a,id) se
FROM t order by a,id --the rows are ordered first by a, then by id
-- same as in the above count analytic function
) s
WHERE se = 1 limit 3; -- the argument 3 or 10 that you wish to pass
-- se = 1 won't change for other ids, it's a marker
-- that id = n is reached
DEMO
I think this will do:
SELECT *
FROM (SELECT * from MyTable where ID > givenId order by A) sub
LIMIT 10;
You don't want the A columns, so:
SELECT r.*
FROM t
WHERE t.id > ANY (SELET id FROM t t2 WHERE t2.col = 'A')
ORDER BY col
LIMIT 10;
Note that this does not return any rows with A as the value. It also works when the comparison value is not sorted first.
this will work:
SELECT * from Table1 where "ID"=1
order by "A" desc limit 2;
check :http://sqlfiddle.com/#!15/5854b/3
for your query :
SELECT * from Table1 where "ID"=1
order by "A" desc limit 10;

Compare every field in table to every other field in same table

Imagine a table with only one column.
+------+
| v |
+------+
|0.1234|
|0.8923|
|0.5221|
+------+
I want to do the following for row K:
Take row K=1 value: 0.1234
Count how many values in the rest of the table are less than or equal to value in row 1.
Iterate through all rows
Output should be:
+------+-------+
| v |output |
+------+-------+
|0.1234| 0 |
|0.8923| 2 |
|0.5221| 1 |
+------+-------+
Quick Update I was using this approach to compute a statistic at every value of v in the above table. The cross join approach was way too slow for the size of data I was dealing with. So, instead I computed my stat for a grid of v values and then matched them to the vs in the original data. v_table is the data table from before and stat_comp is the statistics table.
AS SELECT t1.*
,CASE WHEN v<=1.000000 THEN pr_1
WHEN v<=2.000000 AND v>1.000000 THEN pr_2
FROM v_table AS t1
LEFT OUTER JOIN stat_comp AS t2
Windows functions were added to ANSI/ISO SQL in 1999 and to to Hive in version 0.11, which was released on 15 May, 2013.
What you are looking for is a variation on rank with ties high which in ANSI/ISO SQL:2011 would look like this-
rank () over (order by v with ties high) - 1
Hive currently does not support with ties ... but the logic can be implemented using count(*) over (...)
select v
,count(*) over (order by v) - 1 as rank_with_ties_high_implicit
from mytable
;
or
select v
,count(*) over
(
order by v
range between unbounded preceding and current row
) - 1 as rank_with_ties_high_explicit
from mytable
;
Generate sample data
select 0.1234 as v into #t
union all
select 0.8923
union all
select 0.5221
This is the query
;with ct as (
select ROW_NUMBER() over (order by v) rn
, v
from #t ot
)
select distinct v, a.cnt
from ct ot
outer apply (select count(*) cnt from ct where ct.rn <> ot.rn and v <= ot.v) a
After seeing your edits, it really does look look like you could use a Cartesian product, i.e. CROSS JOIN here. I called your table foo, and crossed joined it to itself as bar:
SELECT foo.v, COUNT(foo.v) - 1 AS output
FROM foo
CROSS JOIN foo bar
WHERE foo.v >= bar.v
GROUP BY foo.v;
Here's a fiddle.
This query cross joins the column such that every permutation of the column's elements is returned (you can see this yourself by removing the SUM and GROUP BY clauses, and adding bar.v to the SELECT). It then adds one count when foo.v >= bar.v, yielding the final result.
You can take the full Cartesian product of the table with itself and sum a case statement:
select a.x
, sum(case when b.x < a.x then 1 else 0 end) as count_less_than_x
from (select distinct x from T) a
, T b
group by a.x
This will give you one row per unique value in the table with the count of non-unique rows whose value is less than this value.
Notice that there is neither a join nor a where clause. In this case, we actually want that. For each row of a we get a full copy aliased as b. We can then check each one to see whether or not it's less than a.x. If it is, we add 1 to the count. If not, we just add 0.

SQL query sum at bottom row

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