SQL-how to find the percentage - sql

I have a table with following values id and amt
id amt amt%
1 500 16.7
2 600 20
3 900 30
4 1000 33.3
-----------------------------
3000 100%
I have to find the amt% for id=1 by dividing amt for id=1/sum(amt for all the ids).
How to write query for this in sql?

select id, amt, 100.0 * amt / (select sum(amt) from tab) as amt_percent
from tab
Mind 100.0, .0 is quite important it will "force" conversion ints to floats.
Added after comment:
select t.id, t.amt, 100.0 * t.amt / p.s as amt_percent
from tab t
join (
select id, sum(amt) as s
from tab
group by id
) p on p.id = t.id
and alternative way:
select id, amt,
100.0 * amt / (
select sum(amt)
from tab t1
where t1.id = t.id
) as amt_percent
from tab t
I prefer joining subquery - the first one.

You can try something like this:
SELECT
id, amt,
amt / (SELECT SUM(amt) FROM tabl) * 100 as amtperc
FROM tabl

select id, amt, 100*amt/(select NULLIF(sum(amt),0) from tbl) as "amt%"
from tbl
or maybe so
select t.id, t.amt, 100*amt/sm.s as "amt%"
from tbl t, (select NULLIF(sum(amt),0) as s from tbl) sm
edit check for dividing by zero

Assuming you are using Oracle (as in your previous question), try:
select id, amt, 100 * amt / sum(amt) over () amtperc
from your_table

select id, amt, 100 * amt/(case when (select sum(amt)as ss from tablename)>0 then
(select sum(amt)as ss from tablename) else 1 end) as [amt%] from tablename

Related

SQL Case MAX WHEN LIKE

I would like to create a case statement or a rank over statement for a particular outlier case I have.
I am not sure how to write a case statement utilizing CASE (pseudo code)
WHEN MAX Total_Revenue COMPANY like 'ABC%'
else COMPANY
i have tried rank over, but it is not working:
,RANK() OVER(COMPANY LIKE 'DEF%' ORDER BY Total_Revenue DESC) AS grp
Current table:
Company Total_Revenue
ABC 10
DEF1 25 --This row will NOT be selected as its less than
DEF2 35 -- this row will be kept
GHI3 65
JKL9 100
Ouput table:
Company Total_Revenue
ABC 10
DEF2 35 --kept
GHI3 65
JKL9 100
There's quite a few ways to do what it seems like you are after:
Using a subquery to find max revenue for each comp:
SELECT Company, Total_Revenue
FROM myTable
INNER JOIN
(
SELECT Left(Company, 3) as leftcomp,
max(Total_Revenue) as maxTotalRevenue
FROM mytable
GROUP BY Left(Company, 3)
) mt
ON Left(myTable.Company, 3) = mt.leftcomp
AND myTable.Total_Revenue = mt.maxTotalRevenue;
Window function that is later filtered by Where:
SELECT *
FROM
(
SELECT Company,
Total_Revenue,
MAX(Total_Revenue) OVER (PARTITION BY Left(Company, 3)) as maxTotalRevenue
FROM myTable
) mt
WHERE Total_Revenue = maxTotalRevenue;
Correlated subquery in the WHERE clause:
SELECT *
FROM myTable mt1
Where Total_Revenue =
(
SELECT max(total_revenue)
FROM myTable
WHERE Left(myTable.Company, 3) = Left(mt1.Company, 3)
);
SQLFiddle here
You seems want row_number() function :
SELECT t.*
FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY LEFT(Company, 3) ORDER BY Total_Revenue DESC) AS Seq
FROM table t
) t
WHERE Seq = 1;
This is what you want ?
select * from t
where
Company not in (
select Company from t where Company like '%DEF%' order by Company desc offset 1
)
use aggregate function max() and sub-query
select * from Yourtable t1
where t1.Total_Revenue in (
select max(T.Total_Revenue ) as Total_Revenue from
(
select case when Company like'%DEF%' then 'DEF'
else Company end as Company ,
Total_Revenue from Yourtable
) as T
group by T.Company
)
http://sqlfiddle.com/#!18/e896c/5
Company Total_Revenue
ABC 10
DEF2 35
GHI3 65
JKL9 100

How to divide the count() of two seperate queries in DB2

So I have
select count(*) from ( "query1")
select count(*) from ( "query2")
I want to divide the two and get the floating point result.
I was told to use something like this
SELECT (COUNT(smtg) * 1.0) / COUNT(smtg)
But Im not sure
You can just do:
select q1.cnt * 1.0 / q2.cnt
from (select count(*) as cnt from ( "query1") ) q1 cross join
(select count(*) as cnt from ( "query2") ) q2;
Or, if you prefer:
select ( (select count(*) from ( "query1")) * 1.0 /
(select count(*) from ( "query2"))
)
from sysibm.sysdummy1;
other solution (be carefull to not divide by 0)
with
query1 as (select count(*) as nb1 from ( "query1")),
query2 as (select count(*) as nb2 from ( "query2"))
select case when nb2=0 then null else nb1* 1.0/nb2 end as Result
from query1, query2

Trying to divide two counts in SQL

so I'm trying to do simple division and of course SQL, being super logical that it is, makes it impossible. What I am trying to do is this:
SELECT * FROM
1 - ((SELECT COUNT(DISTINCT T.DID) FROM TGAMAZING T AS NUM) * 100 /
(SELECT COUNT(DISTINCT D.ID) FROM DIRECTORS D AS DENOM))
but how would I write this SQL (Oracle)?
Oracle
SQL Fiddle: http://sqlfiddle.com/#!4/34298/8
Method 1:
SELECT 1 - (COUNT(DISTINCT DID) * 100 / COUNT(DISTINCT ID))
FROM TGAMAZING
cross join DIRECTORS;
Method 2:
SELECT 1 -
(
(SELECT COUNT(DISTINCT DID) FROM TGAMAZING) * 100 /
(SELECT COUNT(DISTINCT ID) FROM DIRECTORS)
)
FROM DUAL;
SQL Server
SQL Fiddle: http://sqlfiddle.com/#!6/34298/3
Method 1
SELECT 1 - (COUNT(DISTINCT DID) * 100.0 / COUNT(DISTINCT ID))
FROM TGAMAZING
cross join DIRECTORS;
Method 2
SELECT 1 -
(
(SELECT COUNT(DISTINCT DID) FROM TGAMAZING) * 100.0 /
(SELECT COUNT(DISTINCT ID) FROM DIRECTORS)
)
Write the calculation as it were fields and use "FROM DUAL", as you don't want to query any of your tables.
SELECT
1 - ((SELECT COUNT(DISTINCT T.DID) FROM TGAMAZING T) * 100 /
(SELECT COUNT(DISTINCT D.ID) FROM DIRECTORS D))
AS RESULT
FROM DUAL
Reference: Selecting from the DUAL Table.

SQL stored procedure to add up values and stop once the maximum has been reached

I would like to write a SQL query (SQL Server) that will return rows (in a given order) but only up to a given total. My client has paid me a given amount, and I want to return only those rows that are <= to that amount.
For example, if the client paid me $370, and the data in the table is
id amount
1 100
2 122
3 134
4 23
5 200
then I would like to return only rows 1, 2 and 3
This needs to be efficient, since there will be thousands of rows, so a for loop would not be ideal, I guess. Or is SQL Server efficient enough to optimise a stored proc with for loops?
Thanks in advance. Jim.
A couple of options are.
1) Triangular Join
SELECT *
FROM YourTable Y1
WHERE (SELECT SUM(amount)
FROM YourTable Y2
WHERE Y1.id >= Y2.id ) <= 370
2) Recursive CTE
WITH RecursiveCTE
AS (
SELECT TOP 1 id, amount, CAST(amount AS BIGINT) AS Total
FROM YourTable
ORDER BY id
UNION ALL
SELECT R.id, R.amount, R.Total
FROM (
SELECT T.*,
T.amount + Total AS Total,
rn = ROW_NUMBER() OVER (ORDER BY T.id)
FROM YourTable T
JOIN RecursiveCTE R
ON R.id < T.id
) R
WHERE R.rn = 1 AND Total <= 370
)
SELECT id, amount, Total
FROM RecursiveCTE
OPTION (MAXRECURSION 0);
The 2nd one will likely perform better.
In SQL Server 2012 you will be able to so something like
;WITH CTE AS
(
SELECT id,
amount,
SUM(amount) OVER(ORDER BY id
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
AS RunningTotal
FROM YourTable
)
SELECT *
FROM CTE
WHERE RunningTotal <=370
Though there will probably be a more efficient way (to stop the scan as soon as the total is reached)
Straight-forward approach :
SELECT a.id, a.amount
FROM table1 a
INNER JOIN table1 b ON (b.id <=a.id)
GROUP BY a.id, a.amount
HAVING SUM(b.amount) <= 370
Unfortunately, it has N^2 performance issue.
something like this:
select id from
(
select t1.id, t1.amount, sum( t2.amount ) s
from tst t1, tst t2
where t2.id <= t1.id
group by t1.id, t1.amount
)
where s < 370

A simple way to sum a result from UNION in MySQL

I have a union of three tables (t1, t2, t3).
Each rerun exactly the same number of records, first column is id, second amount:
1 10
2 20
3 20
1 30
2 30
3 10
1 20
2 40
3 50
Is there a simple way in SQL to sum it up, i.e. to only get:
1 60
2 80
3 80
select id, sum(amount) from (
select id,amount from table_1 union all
select id,amount from table_2 union all
select id,amount from table_3
) x group by id
SELECT id, SUM(amount) FROM
(
SELECT id, SUM(amount) AS `amount` FROM t1 GROUP BY id
UNION ALL
SELECT id, SUM(amount) AS `amount` FROM t2 GROUP BY id
) `x`
GROUP BY `id`
I groupped each table and unioned because i think it might be faster, but you should try both solutions.
Subquery:
SELECT id, SUM(amount)
FROM ( SELECT * FROM t1
UNION ALL SELECT * FROM t2
UNION ALL SELECT * FROM t3
)
GROUP BY id
Not sure if MySQL uses common table expression but I would do this in postgres:
WITH total AS(
SELECT id,amount AS amount FROM table_1 UNION ALL
SELECT id,amount AS amount FROM table_2 UNION ALL
SELECT id,amount AS amount FROM table_3
)
SELECT id, sum(amount)
FROM total
I think that should do the trick as well.
As it's not very clear from previous answers, remember to give aliases (on MySQL/MariaDb) or you'll get error:
Every derived table must have its own alias
select id, sum(amount) from (
select id,amount from table_1 union all
select id,amount from table_2 union all
select id,amount from table_3
) AS 'aliasWhichIsNeeded'
group by id
Yes!!! Its okay! Thanks!!!!
My code finishing:
SELECT SUM(total)
FROM (
(SELECT 1 as id, SUM(e.valor) AS total FROM entrada AS e)
UNION
(SELECT 1 as id, SUM(d.valor) AS total FROM despesa AS d)
UNION
(SELECT 1 as id, SUM(r.valor) AS total FROM recibo AS r WHERE r.status = 'Pago')
) x group by id
SELECT BANKEMPNAME, workStation, SUM (CALCULATEDAMOUNT) FROM(
SELECT BANKEMPNAME, workStation, SUM(CALCULATEDAMOUNT) AS CALCULATEDAMOUNT,SALARYMONTH
FROM dbo.vw_salaryStatement
WHERE (ITEMCODE LIKE 'A%')
GROUP BY BANKEMPNAME,workStation, SALARYMONTH
union all
SELECT BANKEMPNAME, workStation, SUM(CALCULATEDAMOUNT) AS CALCULATEDAMOUNT,SALARYMONTH
FROM dbo.vw_salaryStatement
WHERE (ITEMCODE NOT LIKE 'A%')
GROUP BY BANKEMPNAME, workStation, SALARYMONTH) as t1
WHERE SALARYMONTH BETWEEN '20220101' AND '20220131'
group by BANKEMPNAME, workStation
order by BANKEMPNAME asc
IN MSSQL You can write this way, But Doing UNION ALL THE Column should be the same for both ways.
I have given this example So that you can understand the process...