Sorry if this is basic but couldn't find this anywhere on stack overflow.
Ive created a temporary table and when i run the query it brings back alot of results. I need to narrow it down further by an amount difference between the 2 columns but im struggling to work out how to do it. The current query is:
select * from #mi where round(avalue,0) <> round(bvalue,0)
I basically want it to say <> 1000 (amounts are not the same but there is 1000 difference between the amounts i want to view)
Currently i am getting figures like this
avalue=10000 bvalue=10000.1
I need it to show as
avalue=10000 bvalue=20001 or bvalue=8999
Thanks
Your question is a little unclear. I think it is:
amounts are not the same but there is 1000 difference between the amounts i want to view
Use - and `abs():
select *
from #mi
where abs(a.value - b.value) < 1000
you can use case when
select t.*,
case when round(avalue,0) <> round(bvalue,0) then round(bvalue+bvalue+1,0)
from #mi t
The problem I'm facing right now is I'm working with a SQL query that has over 200 lines of code and at the moment in multiple cases I'm just repeating the same sub-query multiple times in this select statement. In the code below I'm using two of the select statements a lot "avail_qty" and "pct_avail" which both having equations in them. Inside the LOW_CNT_&% SELECT statement I use both of the previous two SELECT statements over and over (this is just one example in my code). I would like to be able to make the equation once and assign it to a variable. Is there any way of doing this? I have tried using the WITH clause but for that you need to use a FROM clause, my FROM clause is massive and would look just as ugly if I were to use a WITH clause (plus instead of repeating the SELECT statement now I would be just repeating the FROM statement).
The reason I don't want to type out the whole equation multiple times is for a two reasons the first is it makes the code easier to read. My other reason is because multiple people edit this query and if someone else were to edit the equation in one spot but forgets to edit it in another spot, that could be bad. Also it doesn't feel like good code etiquette to repeat code over and over.
SELECT
all_nbr.total_qty,
NVL (avail_nbr.avail_qty, 0) AS avail_qty,
100 * TRUNC ( (NVL (avail_nbr.avail_qty, 0) / all_nbr.total_qty), 2) AS pct_avail,
CASE
WHEN ((NVL (avail_nbr.avail_qty, 0)) < 35)
THEN CASE
WHEN ((100 * TRUNC ( (NVL (avail_nbr.avail_qty, 0) / all_nbr.total_qty), 2)) < 35)
THEN (35 - (NVL (avail_nbr.avail_qty, 0)))
ELSE 0
END
ELSE 0
END AS "LOW_CNT_&%"
FROM
...
Any help would be awesome!!
If the subquery is exactly the same one, you can pre-compute it as a Common Table Expression (CTE). For example:
with
cte1 as (
select ... -- long, tedious, repetitive SELECT here
),
cte2 as (
select ... -- you can reference/use cte1 here
)
select ...
from cte1 -- you can use cte1 here, multiple times if you want
join cte2 -- you can also reference/use cte2 here, also multiple times
join ... -- all other joins
cte1 (you can use any name) is a precomputed table expression that can be used multiple times. You can also have multiple CTEs, each one with different names; also each CTE can reference previous ones.
I have tried using the WITH clause but for that you need to use a FROM clause, my FROM clause is massive and would look just as ugly if I were to use a WITH clause (plus instead of repeating the SELECT statement now I would be just repeating the FROM statement).
You shouldn't need to repeat the from clause. You move all of the query, including that clause, into the CTE; you just pull out the bits that rely on earlier calculations into the main query, which avoids the code repetition.
The structure would be something like:
WITH cte AS (
SELECT
all_nbr.total_qty,
NVL (avail_nbr.avail_qty, 0) AS avail_qty,
100 * TRUNC ( (NVL (avail_nbr.avail_qty, 0) / all_nbr.total_qty), 2) AS pct_avail,
FROM
...
)
SELECT
cte.total_qty,
cte.avail_qty,
cte.pct_avail,
CASE
WHEN cte.avail_qty, 0 < 35
THEN CASE
WHEN cte.total_qty < 35
THEN 35 - cte.avail_qty
ELSE 0
END
ELSE 0
END AS "LOW_CNT_&%"
FROM
cte;
Your main query only need to refer to the CTE (again, based on what you've shown), and can (only) refer to the prjoection of the CTE, incuding the calculated columns. It can't see the underlying tables, but shouldn't need to.
Or with an inline view instead, the principal is the same:
SELECT
total_qty,
avail_qty,
pct_avail,
CASE
WHEN avail_qty < 35
THEN CASE
WHEN total_qty < 35
THEN 35 - avail_qty
ELSE 0
END
ELSE 0
END AS "LOW_CNT_&%"
FROM
(
SELECT
all_nbr.total_qty,
NVL (avail_nbr.avail_qty, 0) AS avail_qty,
100 * TRUNC ( (NVL (avail_nbr.avail_qty, 0) / all_nbr.total_qty), 2) AS pct_avail,
FROM
...
);
Im looking for something like SELECT PRODUCT(table.price) FROM table GROUP BY table.sale similar to how SUM works.
Have I missed something on the documentation, or is there really no PRODUCT function?
If so, why not?
Note: I looked for the function in postgres, mysql and mssql and found none so I assumed all sql does not support it.
For MSSQL you can use this. It can be adopted for other platforms: it's just maths and aggregates on logarithms.
SELECT
GrpID,
CASE
WHEN MinVal = 0 THEN 0
WHEN Neg % 2 = 1 THEN -1 * EXP(ABSMult)
ELSE EXP(ABSMult)
END
FROM
(
SELECT
GrpID,
--log of +ve row values
SUM(LOG(ABS(NULLIF(Value, 0)))) AS ABSMult,
--count of -ve values. Even = +ve result.
SUM(SIGN(CASE WHEN Value < 0 THEN 1 ELSE 0 END)) AS Neg,
--anything * zero = zero
MIN(ABS(Value)) AS MinVal
FROM
Mytable
GROUP BY
GrpID
) foo
Taken from my answer here: SQL Server Query - groupwise multiplication
I don't know why there isn't one, but (take more care over negative numbers) you can use logs and exponents to do:-
select exp (sum (ln (table.price))) from table ...
There is no PRODUCT set function in the SQL Standard. It would appear to be a worthy candidate, though (unlike, say, a CONCATENATE set function: it's not a good fit for SQL e.g. the resulting data type would involve multivalues and pose a problem as regards first normal form).
The SQL Standards aim to consolidate functionality across SQL products circa 1990 and to provide 'thought leadership' on future development. In short, they document what SQL does and what SQL should do. The absence of PRODUCT set function suggests that in 1990 no vendor though it worthy of inclusion and there has been no academic interest in introducing it into the Standard.
Of course, vendors always have sought to add their own functionality, these days usually as extentions to Standards rather than tangentally. I don't recall seeing a PRODUCT set function (or even demand for one) in any of the SQL products I've used.
In any case, the work around is fairly simple using log and exp scalar functions (and logic to handle negatives) with the SUM set function; see #gbn's answer for some sample code. I've never needed to do this in a business application, though.
In conclusion, my best guess is that there is no demand from SQL end users for a PRODUCT set function; further, that anyone with an academic interest would probably find the workaround acceptable (i.e. would not value the syntactic sugar a PRODUCT set function would provide).
Out of interest, there is indeed demand in SQL Server Land for new set functions but for those of the window function variety (and Standard SQL, too). For more details, including how to get involved in further driving demand, see Itzik Ben-Gan's blog.
You can perform a product aggregate function, but you have to do the maths yourself, like this...
SELECT
Exp(Sum(IIf(Abs([Num])=0,0,Log(Abs([Num])))))*IIf(Min(Abs([Num]))=0,0,1)*(1-2*(Sum(IIf([Num]>=0,0,1)) Mod 2)) AS P
FROM
Table1
Source: http://productfunctionsql.codeplex.com/
There is a neat trick in T-SQL (not sure if it's ANSI) that allows to concatenate string values from a set of rows into one variable. It looks like it works for multiplying as well:
declare #Floats as table (value float)
insert into #Floats values (0.9)
insert into #Floats values (0.9)
insert into #Floats values (0.9)
declare #multiplier float = null
select
#multiplier = isnull(#multiplier, '1') * value
from #Floats
select #multiplier
This can potentially be more numerically stable than the log/exp solution.
I think that is because no numbering system is able to accommodate many products. As databases are designed for large number of records, a product of 1000 numbers would be super massive and in case of floating point numbers, the propagated error would be huge.
Also note that using log can be a dangerous solution. Although mathematically log(a*b) = log(a)*log(b), it might not be in computers as we are not dealing with real numbers. If you calculate 2^(log(a)+log(b)) instead of a*b, you may get unexpected results. For example:
SELECT 9999999999*99999999974482, EXP(LOG(9999999999)+LOG(99999999974482))
in Sql Server returns
999999999644820000025518, 9.99999999644812E+23
So my point is when you are trying to do the product do it carefully and test is heavily.
One way to deal with this problem (if you are working in a scripting language) is to use the group_concat function.
For example, SELECT group_concat(table.price) FROM table GROUP BY table.sale
This will return a string with all prices for the same sale value, separated by a comma.
Then with a parser you can get each price, and do a multiplication. (In php you can even use the array_reduce function, in fact in the php.net manual you get a suitable example).
Cheers
Another approach based on fact that the cardinality of cartesian product is product of cardinalities of particular sets ;-)
⚠ WARNING: This example is just for fun and is rather academic, don't use it in production! (apart from the fact it's just for positive and practically small integers)⚠
with recursive t(c) as (
select unnest(array[2,5,7,8])
), p(a) as (
select array_agg(c) from t
union all
select p.a[2:]
from p
cross join generate_series(1, p.a[1])
)
select count(*) from p where cardinality(a) = 0;
The problem can be solved using modern SQL features such as window functions and CTEs. Everything is standard SQL and - unlike logarithm-based solutions - does not require switching from integer world to floating point world nor handling nonpositive numbers. Just number rows and evaluate product in recursive query until no row remain:
with recursive t(c) as (
select unnest(array[2,5,7,8])
), r(c,n) as (
select t.c, row_number() over () from t
), p(c,n) as (
select c, n from r where n = 1
union all
select r.c * p.c, r.n from p join r on p.n + 1 = r.n
)
select c from p where n = (select max(n) from p);
As your question involves grouping by sale column, things got little bit complicated but it's still solvable:
with recursive t(sale,price) as (
select 'multiplication', 2 union
select 'multiplication', 5 union
select 'multiplication', 7 union
select 'multiplication', 8 union
select 'trivial', 1 union
select 'trivial', 8 union
select 'negatives work', -2 union
select 'negatives work', -3 union
select 'negatives work', -5 union
select 'look ma, zero works too!', 1 union
select 'look ma, zero works too!', 0 union
select 'look ma, zero works too!', 2
), r(sale,price,n,maxn) as (
select t.sale, t.price, row_number() over (partition by sale), count(1) over (partition by sale)
from t
), p(sale,price,n,maxn) as (
select sale, price, n, maxn
from r where n = 1
union all
select p.sale, r.price * p.price, r.n, r.maxn
from p
join r on p.sale = r.sale and p.n + 1 = r.n
)
select sale, price
from p
where n = maxn
order by sale;
Result:
sale,price
"look ma, zero works too!",0
multiplication,560
negatives work,-30
trivial,8
Tested on Postgres.
Here is an oracle solution for anyone who needs it
with data(id, val) as(
select 1,1.0 from dual union all
select 2,-2.0 from dual union all
select 3,1.0 from dual union all
select 4,2.0 from dual
),
neg(val , modifier) as(
select exp(sum(ln(abs(val)))), case when mod(count(*),2) = 0 then 1 Else -1 end
from data
where val <0
)
,
pos(val) as (
select exp(sum(ln(val)))
from data
where val >=0
)
select (select val*modifier from neg)*(select val from pos) product from dual
I have an SQL statement that counts over the total number of rows active packages whose end date is null. I am currently doing this using (x/y) * 100:
SELECT (SELECT COUNT(*)
FROM packages
WHERE end_dt IS NULL) / (SELECT COUNT(*)
FROM packages) * 100
FROM DUAL;
I wonder if there is a way to make use of any Oracle function to express this more easily?
There's no functionality I'm aware of, but you could simply the query to be:
SELECT SUM(CASE WHEN p.end_dt IS NULL THEN 1 ELSE 0 END) / COUNT(*) * 100
FROM PACKAGES p
So, basically the formula is
COUNT(NULL-valued "end_dt") / COUNT(*) * 100
Now, COUNT(NULL-valued "end_dt") is syntactically wrong, but it can be represented as COUNT(*) - COUNT(end_dt). So, the formula can be like this:
(COUNT(*) - COUNT(end_dt)) / COUNT(*) * 100
If we just simplify it a little, we'll get this:
SELECT (1 - COUNT(end_dt) * 1.0 / COUNT(*)) * 100 AS Percent
FROM packages
The * 1.0 bit converts the integer result of COUNT to a non-integer value so make the division non-integer too.
The above sentence and the corresponding part of the script turned out to be complete rubbish. Unlike some other database servers, Oracle does not perform integer division, even if both operands are integers. This doc page contains no hint of such behaviour of the division operator.
The original post is a little long in the tooth but this should work, using the function "ratio_to_report" that's been available since Oracle 8i:
SELECT
NVL2(END_DT, 'NOT NULL', 'NULL') END_DT,
RATIO_TO_REPORT(COUNT(*)) OVER () AS PCT_TOTAL
FROM
PACKAGES
GROUP BY
NVL2(END_DT, 'NOT NULL', 'NULL');
Ok, so I'm trying to select an amount of rows from a column that holds the value 3, but only if there are no rows containing 10 or 4, if there are rows containing 10 or 4 I only want to show those.
What would be a good syntax to do that? So far I've been attempting a CASE WHEN statement, but I can't seem to figure it out.
Any help would be greatly appreciated.
(My database is in an MS SQL 2008 server)
Use a union all:
select
// columns
from YourTable
where YourColumn = 3 and not exists (
select 1 from YourTable where YourColumn = 10 or YourColumn = 4)
union all
select
// columns
from YourTable
where YourColumn = 10 or YourColumn = 4
FYI: Orginal question title was "SQL CASE WHEN NULL - question"
CASE WHEN YourColumn IS NULL THEN x ELSE y END
Since there is nothing that compares to NULL and returns true (not even NULL itself), you cant't do
CASE YourColumn WHEN NULL THEN x ELSE y END
only
CASE ISNULL(YourColumn, '') WHEN '' THEN x ELSE y END
but then you lose the ability to differentiate between NULL and the (in this example) empty string.
Depending on the size of your table and its indexes, it may be more efficient to calculate which values you want before the query
declare #UseThree as bit = 1;
if exists (select 1 from testtable where rowval in (10,4))
set #UseThree = 0;
select COUNT(*)
from testtable
where (#UseThree = 1 AND rowval=3)
OR
(#UseThree = 0 AND rowval in (10,4))
The simplest solution would be to do this in two queries:
SELECT ... FROM YourTable WHERE SomeColumn IN (10,4)
If and only if the above query yields no results, then run the second query:
SELECT ... FROM YourTable WHERE SomeColumn = 3
Running two queries may seem "inelegant" but it has advantages:
It's easy to code
It's easy to debug
It often has better performance than a very complex solution
It's easy to understand for a programmer who has to maintain the code after you.
Running two queries may seem like it has extra overhead, but also consider that you won't run the second query every time -- only if the first query has an empty result. If you use an expensive single-query solution, remember that it will incur that expense every time.