SQL Query: get adjusted value of each row - sql

I have several columns in a SQL table, and I would like to compute the result for each row in the table. My data looks something like this:
data
name value adjustor1 adjustor2
Comp1 20 0.05 0.08
Comp2 80 -0.07 0.065
The formula for the adjusted value for each row is:
adjusted_value = value*(1 + adjustor1)*(1 + adjustor2)*(100/sum(value))
So the adjusted output should be:
data
name adjusted_value
Comp1 22.25
Comp2 77.75
The original values sum to 100, and the adjusted values should also sum to 100. I've tried things such as:
SELECT adjusted*(100/sum(adjusted))
FROM (
SELECT value*(1+adjustor1)*(1+adjustor2) as adjusted
FROM data
) as result
which gives me the error: ERROR: column "result.adjusted" must appear in the GROUP BY clause or be used in an aggregate function
Although if I just do:
SELECT sum(adjusted)
FROM (
SELECT value*(1+adjustor1)*(1+adjustor2) as adjusted
FROM data
) as result
OR
SELECT adjusted
FROM (
SELECT value*(1+adjustor1)*(1+adjustor2) as adjusted
FROM data
) as result
I can get either the sum OR the adjusted value, but not both.

Column name value is not a good choice, it is a reserved word. I changed it to val instead.
This is the query you need:
WITH data(name,val,adjustor1,adjustor2) AS (
VALUES
('Comp1'::text,20,0.05,0.08),
('Comp2',80,-0.07,0.065)
)
SELECT name,val,adjustor1,adjustor2,
CAST(val*(1+adjustor1)*(1+adjustor2)*(100/sum(
val*(1+adjustor1)*(1+adjustor2)
) OVER ()) AS numeric(10,3)) adjusted_value
FROM data;
I must admit, it is a bit clumsy, sub-query makes it easier to understand (SQL Fiddle):
WITH data(name,val,adjustor1,adjustor2) AS (
VALUES
('Comp1'::text,20,0.05,0.08),
('Comp2',80,-0.07,0.065)
)
SELECT name, CAST(adj*(100/sum(adj) OVER ()) AS numeric(8,3)) adjusted_value
FROM (
SELECT name,val,adjustor1,adjustor2,
val*(1+adjustor1)*(1+adjustor2) adj
FROM data) s;
Some notes:
Original sum(adj) is replaced with sum(adj) OVER (), which is a syntax for window functions.
I also added [CAST()][3] in order to round up the values.

you can get the SUM in a subquery and then you can do CROSS JOIN to achieve what you want
SELECT name,
value*(1+adjustor1)*(1+adjustor2)*(100/T.adjSum) as adjusted_value
FROM data
CROSS JOIN
(SELECT SUM(value*(1+adjustor1)*(1+adjustor2)) as adjSum
FROM data) T

Related

PostgreSQL Error more than one row returned by a subquery used as an expression

I have a problem with a data query where I query a single column like this:
SELECT a.ad_morg_key, count(a.sid_mpenduduk_key) AS total_population
FROM sid_mpenduduk a
GROUP BY a.ad_morg_key;
and it really works. But when I query with multiple columns with a query like this:
SELECT a.ad_morg_key, b."name",
count(b.sid_magama_key) AS total,
count(b.sid_magama_key)::float / (SELECT count(a.sid_mpenduduk_key)
FROM sid_mpenduduk a
GROUP BY a.ad_morg_key)::float * 100::float AS percentage,
(SELECT count(a.sid_mpenduduk_key) FROM sid_mpenduduk a GROUP BY a.ad_morg_key) AS total_population
FROM sid_mpenduduk a
INNER JOIN sid_magama b ON a.sid_magama_key = b.sid_magama_key
GROUP BY a.ad_morg_key, b."name";
But it fails with:
ERROR: more than one row returned by a subquery used as an expression
I want the final result like this :
Your subquery is grouped by a.ad_morg_key so it will get you a row for each different value of a.ad_morg_key.
In general terms each subquery in a SELECT statement should return a single value. Suppose you have the following table called A.
A_key
A_value
A1
200
A2
200
If you execute
SELECT (SELECT A_KEY FROM A) as keys
FROM A
the subquery (SELECT A_KEY FROM A) returns
A_key
A1
A2
so what should be the value for keys?
SQL cannot handle this decision so you should pick one of the values or aggregate them into a single value.
Use a correlation clause instead:
(SELECT count(a.sid_mpenduduk_key)
FROM sid_mpenduduk a2
WHERE a2.ad_morg_key = a.ad_morg_key
) AS total_population
I'm not sure if the subquery is really necessary. So, you might consider asking a new question with sample data, desired results, and a clear explanation of what you are trying to do.
You're getting burned by
GROUP BY ...
(SELECT count(a.sid_mpenduduk_key)
FROM sid_mpenduduk a
GROUP BY a.ad_morg_key) AS total_population
because the outer GROUP BY wants a scalar, but the subquery is producing a count for each a.ad_morg_key.
I don't write my queries that way. Instead, produce a virtual table,
SELECT a.ad_morg_key, b."name",
...
JOIN
(SELECT ad_morg_key,
count(sid_mpenduduk_key) as N
FROM sid_mpenduduk
GROUP BY ad_morg_key) AS morgs
on ad_morg_key = morgs.ad_morg_key
That way, you have the count for each row as N, and you can divide at will,
count(b.sid_magama_key)::float / morgs.N
and, if you get tripped up, you'll have many more rows than you expected instead of an error message.

Query to return the amount of time each field equals a true value

I'm collecting data and storing in SQL Server. The table consist of the data shown in the Example Table below. I need to show the amount of time that each field [Fault0-Fault10] is in a fault state. The fault state is represented as a 1 for the fields value.
I have used the following query and got the desired results. However this is for only one field. I need to have the total time for each field in one query. I'm having issues pulling all of the fields into one query.
SELECT
Distinct [Fault0]
,datediff(mi, min(DateAndTime), max(DateAndTime)) TotalTimeInMins
FROM [dbo].[Fault]
Where Fault0 =1
group by [Fault0]
Results
Assuming you want the max() - min(), then you simply need to unpivot the data. Your query can look like:
SELECT v.faultname,
datediff(minute, min(t.DateAndTime), max(t.DateAndTime)) as TotalTimeInMins
FROM [dbo].[Fault] f CROSS APPLY
(VALUES ('fault0', fault0), ('fault1', fault1), . . ., ('fault10', fault10)
) v(faultname, value)
WHERE v.value = 1
GROUP BY v.faultname;

Emulate subquery with no main table in access

I can do this in SQL Server:
SELECT 'HERRAMIENTA ELÉCTRICA' AS TIPO_PRODUCTO,
0 AS DEPRECIACION,
(select sum(empid) from HR.employees) STOCK
but in Access the same query show me the next error:
Query input must contain at least one table or query
So which could be the best form to emulate this? Make a query with any other table looks dirty for me.
EDIT 1:, HR.employees It may no have data, but i want show constants ('HERRAMIENTA ELÉCTRICA',''0') and 0 in the third column, maybe using isnull and this is not the problem here.
Why not to select directly:
select 'HERRAMIENTA ELÉCTRICA' AS TIPO_PRODUCTO,
0 AS DEPRECIACION,
IIF(ISNULL(sum(empid)), 0, sum(empid)) AS STOCK
from HR.employees
This simply doesn't work in Access. You need a FROM clause.
So you need to have a dummy table with one record, even if you don't use a single field from that table.
SELECT 'HERRAMIENTA ELÉCTRICA' AS TIPO_PRODUCTO,
0 AS DEPRECIACION,
(select sum(empid) from HR.employees) STOCK
FROM Dummy_Table
Using this example as empty table:
with employ as
(select 2 as col from dual
minus
select 2 as col from dual)
The query is this one:
select 'HERRAM' as tipo,
0 as deprec,
coalesce(sum(col), 0) as STOCK
from employ;
coalesce(x, value) sets the column to value when X is null
In Access, you can use a system table, and Val and Nz for the zero value:
SELECT TOP 1
'HERRAMIENTA ELÉCTRICA' AS TIPO_PRODUCTO,
0 AS DEPRECIACION,
Val(Nz((select sum(empid) from HR.employees), 0)) AS STOCK
FROM
MSysObjects

How to use subquery result as the column name of another query

I want to use the result from subquery as the column name of another query since the data changes column all the time and the subquery will decide which column the current forcast data stored. My example:
select item,
item_type
...
forcast_0 * 0.9 as finalforcast
forcast_0 * 0.8 as newforcast
from sales_data.
but the forcast_0 column is the result (fore_column_name) of the subquery, the result may change to forcast_1 or forcast2
select
fore_column_name
from forecast_history
where ...
Also, the forcast column will be used multiple times in the first query. how could I implement this?
Use your sub query as an inline table. Something like....
select item,
item_type,
..
decode(fore_column_name, 'foo', 1, 2) * 0.9 as finalforcast,
decode(fore_column_name, 'foo', 1, 2) * 0.8 as newforcast
from sales_data,
(
select fore_column_name
from forecast_history
where ...
) inlineTable
I'm assuming here that the value from the sub-query will be the same for each row - so a quick cross-join will suffice. If the value will vary depending on the values in each row of the sales_data table, then some other type of join would be more appropriate.
Quick link to decode - in case you aren't familiar with it.

Doing Math with 2 Subquerys

I have two subquerys both calculating sums. I would like to do an Artithmetic Minus(-) with the result of both Querys . eg Query1: 400 Query2: 300 Result should be 100.
Obvious a basic - in the query does not work. The minus works as MINUS on sets. How can I solve this? Do you have any ideas?
SELECT CustumersNo FROM Custumers WHERE
(
SELECT SUM(value) FROM roe WHERE roe.credit = Custumers.CustumersNo
-
SELECT SUM(value) FROM roe WHERE roe.debit = Custumers.CustumersNo
)
> 500
Using Informix - sorry missed that point
To get the original syntax to work, you would need to surround the sub-selects in parentheses:
SELECT CustumersNo
FROM Custumers
WHERE ((SELECT SUM(value) FROM roe WHERE roe.credit = Custumers.CustumersNo)
-
(SELECT SUM(value) FROM roe WHERE roe.debit = Custumers.CustumersNo)
) > 500
Note that aggregates are defined to ignore nulls in the values they aggregate in standard SQL. However, the SUM of an empty set of rows is NULL, not zero.
You can get inventive and devise ways to always have a value for each customer listed in the roe table, such as:
SELECT CustomersNo
FROM (SELECT CustomersNo, SUM(value) AS net_credit
FROM (SELECT credit AS CustomersNo, +value
UNION
SELECT debit AS CustomersNo, -value
) AS x
GROUP BY CustomersNo
) AS y
WHERE net_credit > 500;
You can also do that with an appropriate HAVING clause if you wish. Note that this avoids issues with customers who have credit entries but no debit entries or vice versa; all the entries that are present are treated appropriately.
Your misspelling (or unorthodox spelling) of 'customers' is nearly as good as 'costumers'.
Something like what you tried should work. It may be a syntax problem, and it may depend on what type of SQL you are using. However, an approach like this would be more efficient:
Update: I see you were having a problem with nulls, so I updated it to handle nulls properly.
select CustumersNo from (
select CustumersNo,
sum(coalesce(roecredit.value,0)) - sum(coalesce(roedebit.value,0))
as balance
FROM Custumers
join roe roecredit on roe.credit = Custumers.CustumersNo
join roe roedebit on roe.debit = Custumers.CustumersNo
group by CustumersNo
)
where balance > 500
Caveat: I don't have experience with Informix specifically.