I'm having trouble trying to explain my necessity, so I'll describe the scenario.
Scenario:
Product A has a maximum production of 125KG at a time.
The operator received a production order of 1027,5KG of product A.
The operator have to calculate how many rounds he'll have to
manufacture and adjust the components quantity for each round.
We want to create a report where this calculations are already done and what we believe would be the first step, based on the values of this scenario, is to return something like this:
ROUND QUANTITY(KG)
1 125
2 125
3 125
4 125
5 125
6 125
7 125
8 125
9 27,5
After that, the recalculation of the components could be done with simple operations.
The problem is that we couldn't think of a way to get the desired return and we also couldn't think of a different way of achieving the said report.
All we could do is get the integer part of the division
SELECT FLOOR(1027.5/125) AS "TEST" FROM DUMMY
and the remainder
SELECT MOD(1027.5,125) AS "TEST" FROM DUMMY
We are using:
SAP HANA SQL
Crystal Reports
SAP B1
Any help would be appreciated
Thanks in advance!
There are several ways to achieve want you described.
One way is to translate the requirement into a function that takes the two input parameter values and returns the table of production rounds.
This can look like this:
create or replace function production_rounds(
IN max_production_volume_per_round decimal (10, 2)
, IN production_order_volume decimal (10, 2)
)
returns table (
production_round integer
, production_volume decimal (10, 2))
as
begin
declare rounds_to_produce integer;
declare remainder_production_volume decimal (10, 2);
rounds_to_produce := floor( :production_order_volume / :max_production_volume_per_round);
remainder_production_volume := mod(:production_order_volume, :max_production_volume_per_round);
return
select /* generate rows for all "max" rounds */
s.element_number as production_round
, :max_production_volume_per_round as production_volume
from
series_generate_integer
(1, 1, :rounds_to_produce + 1) s
UNION ALL
select /* generate a row for the final row with the remainder */
:rounds_to_produce + 1 as production_round
, :remainder_production_volume as production_volume
from
dummy
where
:remainder_production_volume > 0.0;
end;
You can use this function just like any table - but with parameters:
select * from production_rounds (125 , 1027.5) ;
PRODUCTION_ROUND PRODUCTION_VOLUME
1 125
2 125
3 125
4 125
5 125
6 125
7 125
8 125
9 27.5
The bit that probably needs explanation is SERIES_GENERATE_INTEGER. This is a HANA-specific built-in function that returns a number of records from a "series". Series here is a sequence of periods within a min and max limit and with a certain step-size between two adjacend periods.
More on how this works can be found in the HANA reference documentation, but for now just say, this is the fastest way to generate a result set with X rows.
This series-generator is used to create all "full" production rounds.
For the second part of the UNION ALL then creates just a single row by selecting from the built-in table DUMMY (DUAL in Oracle) which is guaranteed to only have a single record.
Finally, this second part needs to be "disabled" if there actually is no remainder, which is done by the WHERE clause.
Related
In SQL there are aggregation operators, like AVG, SUM, COUNT. Why doesn't it have an operator for multiplication? "MUL" or something.
I was wondering, does it exist for Oracle, MSSQL, MySQL ? If not is there a workaround that would give this behaviour?
By MUL do you mean progressive multiplication of values?
Even with 100 rows of some small size (say 10s), your MUL(column) is going to overflow any data type! With such a high probability of mis/ab-use, and very limited scope for use, it does not need to be a SQL Standard. As others have shown there are mathematical ways of working it out, just as there are many many ways to do tricky calculations in SQL just using standard (and common-use) methods.
Sample data:
Column
1
2
4
8
COUNT : 4 items (1 for each non-null)
SUM : 1 + 2 + 4 + 8 = 15
AVG : 3.75 (SUM/COUNT)
MUL : 1 x 2 x 4 x 8 ? ( =64 )
For completeness, the Oracle, MSSQL, MySQL core implementations *
Oracle : EXP(SUM(LN(column))) or POWER(N,SUM(LOG(column, N)))
MSSQL : EXP(SUM(LOG(column))) or POWER(N,SUM(LOG(column)/LOG(N)))
MySQL : EXP(SUM(LOG(column))) or POW(N,SUM(LOG(N,column)))
Care when using EXP/LOG in SQL Server, watch the return type http://msdn.microsoft.com/en-us/library/ms187592.aspx
The POWER form allows for larger numbers (using bases larger than Euler's number), and in cases where the result grows too large to turn it back using POWER, you can return just the logarithmic value and calculate the actual number outside of the SQL query
* LOG(0) and LOG(-ve) are undefined. The below shows only how to handle this in SQL Server. Equivalents can be found for the other SQL flavours, using the same concept
create table MUL(data int)
insert MUL select 1 yourColumn union all
select 2 union all
select 4 union all
select 8 union all
select -2 union all
select 0
select CASE WHEN MIN(abs(data)) = 0 then 0 ELSE
EXP(SUM(Log(abs(nullif(data,0))))) -- the base mathematics
* round(0.5-count(nullif(sign(sign(data)+0.5),1))%2,0) -- pairs up negatives
END
from MUL
Ingredients:
taking the abs() of data, if the min is 0, multiplying by whatever else is futile, the result is 0
When data is 0, NULLIF converts it to null. The abs(), log() both return null, causing it to be precluded from sum()
If data is not 0, abs allows us to multiple a negative number using the LOG method - we will keep track of the negativity elsewhere
Working out the final sign
sign(data) returns 1 for >0, 0 for 0 and -1 for <0.
We add another 0.5 and take the sign() again, so we have now classified 0 and 1 both as 1, and only -1 as -1.
again use NULLIF to remove from COUNT() the 1's, since we only need to count up the negatives.
% 2 against the count() of negative numbers returns either
--> 1 if there is an odd number of negative numbers
--> 0 if there is an even number of negative numbers
more mathematical tricks: we take 1 or 0 off 0.5, so that the above becomes
--> (0.5-1=-0.5=>round to -1) if there is an odd number of negative numbers
--> (0.5-0= 0.5=>round to 1) if there is an even number of negative numbers
we multiple this final 1/-1 against the SUM-PRODUCT value for the real result
No, but you can use Mathematics :)
if yourColumn is always bigger than zero:
select EXP(SUM(LOG(yourColumn))) As ColumnProduct from yourTable
I see an Oracle answer is still missing, so here it is:
SQL> with yourTable as
2 ( select 1 yourColumn from dual union all
3 select 2 from dual union all
4 select 4 from dual union all
5 select 8 from dual
6 )
7 select EXP(SUM(LN(yourColumn))) As ColumnProduct from yourTable
8 /
COLUMNPRODUCT
-------------
64
1 row selected.
Regards,
Rob.
With PostgreSQL, you can create your own aggregate functions, see http://www.postgresql.org/docs/8.2/interactive/sql-createaggregate.html
To create an aggregate function on MySQL, you'll need to build an .so (linux) or .dll (windows) file. An example is shown here: http://www.codeproject.com/KB/database/mygroupconcat.aspx
I'm not sure about mssql and oracle, but i bet they have options to create custom aggregates as well.
You'll break any datatype fairly quickly as numbers mount up.
Using LOG/EXP is tricky because of numbers <= 0 that will fail when using LOG. I wrote a solution in this question that deals with this
Using CTE in MS SQL:
CREATE TABLE Foo(Id int, Val int)
INSERT INTO Foo VALUES(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)
;WITH cte AS
(
SELECT Id, Val AS Multiply, row_number() over (order by Id) as rn
FROM Foo
WHERE Id=1
UNION ALL
SELECT ff.Id, cte.multiply*ff.Val as multiply, ff.rn FROM
(SELECT f.Id, f.Val, (row_number() over (order by f.Id)) as rn
FROM Foo f) ff
INNER JOIN cte
ON ff.rn -1= cte.rn
)
SELECT * FROM cte
Not sure about Oracle or sql-server, but in MySQL you can just use * like you normally would.
mysql> select count(id), count(id)*10 from tablename;
+-----------+--------------+
| count(id) | count(id)*10 |
+-----------+--------------+
| 961 | 9610 |
+-----------+--------------+
1 row in set (0.00 sec)
I am looking for a simple clean method to obtain the sequence {1, 2, 3, 4, 5, 6, 7,...,1000000} in MS Access SQL. I thought of creating a table with a column that numbers from 1 to 100000 however, this is inefficient.
Is there a way of generating numbers 1 to 10000000 in MS Access using SQL?
I tried the GENERATE_SERIES() function but MS Access SQL does not support this function.
id | number
------------
1. | 1
2. | 2
3. | 3
4. | 4
5. | 5
6. | 6
7. | 7
8. | 8
Yes, and it is not painfull - use a Cartesian query.
First, create a small query returning 10 records:
SELECT
DISTINCT Abs([id] Mod 10) AS N
FROM
MSysObjects;
Save it as Ten.
Then run this simple query:
SELECT
[Ten_0].[N]+[Ten_1].[N]*10+[Ten_2].[N]*100+[Ten_3].[N]*1000+[Ten_4].[N]*10000+[Ten_5].[N]*100000 AS Id
FROM
Ten AS Ten_0,
Ten AS Ten_1,
Ten AS Ten_2,
Ten AS Ten_3,
Ten AS Ten_4,
Ten AS Ten_5
which, in two seconds, will return Id from 0 to 999999.
Very painful but you can do the following:
create table numbers (
id int autoincrement,
number int
);
-- 1 row
insert into numbers (number) values (1);
-- 2 rows
insert into numbers (number) select number from numbers;
-- 4 rows
insert into numbers (number) select number from numbers;
-- 8 rows
insert into numbers (number) select number from numbers;
-- repeat a total of 20 times
The value of number is always 1, but id increments. You can make them equal using an update:
update numbers
set number = id;
If this were SQL server you could use a recursive CTE to do this.
WITH number
AS
(
SELECT num = 1
UNION ALL
SELECT num + 1
from number
where num < 1000000
)
SELECT num FROM number
option(maxrecursion 0)
But you are asking about MS Access, since access does not support recursive CTEs, you can try doing it with a macro and insert it into a table and read it out?
I want to write a statement that generates a random pick from a set of four values (2,4,6, and 8). Below is the select statement that I have so far
SELECT
CASE
WHEN RN_GENERATOR.RANDOM_NUMBER BETWEEN 0 AND 2.00 THEN 2
WHEN RN_GENERATOR.RANDOM_NUMBER BETWEEN 2.01 AND 4.00 THEN 4
WHEN RN_GENERATOR.RANDOM_NUMBER BETWEEN 4.01 AND 6.00 THEN 6
WHEN RN_GENERATOR.RANDOM_NUMBER BETWEEN 6.01 AND 8.00 THEN 8
END AS ORDER_FREQUENCY
FROM (SELECT ROUND(RAND()*8,2) AS RANDOM_NUMBER FROM DUMMY) RN_GENERATOR
is there a more intelligent way of doing this?
Looks to me as if your requirement can be fulfilled but his statement
select
ROUND(rand()*4, 0, ROUND_CEILING) * 2 as ORDER_FREQUENCY
from dummy;
RAND() * 4 spreads the value range of possible outcomes for the RAND() function from 0..1 to 0..4.
ROUND( ... , 0, ROUND_CEILING) rounds the number to the next larger or equal integer and leaves no decimal places. For this example this means that the output of this rounding can only be 1, 2, 3 or 4.
*2 simply maps the four possible values to your target number range 2, 4, 6, 8. If the multiplication wouldn't suffice, you could also use the MAP() function for this.
And that's it. Random numbers picked from the set of (2, 4, 6, 8).
You can randomly sort a data set by using
ORDER BY Rand()
and select first one as your random value
Here is an example
select top 1 rownum
from Numbers_Table(10) as nt
where rownum in (2,4,6,8)
order by rand();
Numbers_Table function returns a numbers table on HANA database and I filter only the values that you want to see as your possible random values using WHERE clause
TOP 1 clause in SELECT command returns the first integer which is randomly ordered
I hope it helps
I have a table like;
**ID** **CASH** **INTERVAL**
1 60 5
2 10 3
3 20 4
I want to add 2 columns deriving from current ones like; Column MULT means I list numbers from 1 to INTERVAL by commas and for VAL value I substract CASH from 100 and divide it by INTERVAL and list those intervals by comma listed values inside column VAL
**ID** **CASH** **INTERVAL** **MULT** **VAL**
1 60 5 1,2,3,4,5 8,8,8,8,8
2 10 3 1,2,3 30,30,30
3 20 4 1,2,3,4 20,20,20,20
I know it looks like not an informative question but at least anyone know about to list them in single column with commas using STUFF or etc?
Given how you phrase the question and the sample data you provide, I would be tempted to use a very bespoke approach for this:
with params as (
select '1,2,3,4,5,6,7,8,9' as numbers,
'x,x,x,x,x,x,x,x,x' as vals
)
select l.*,
left(numbers, interval * 2 - 1) as mult,
replace(left(vals, interval * 2 - 1), 'x', (100 - cash) / interval) as val
from params cross join
[like] l;
Of course, you might need to extend the strings in the CTE, if they are not long enough (and this might affect the arithmetic).
The advantage to this approach is speed. It should be pretty fast.
Note: you can also use replicate() rather than the vals.
I have a Select query:
SELECT
SAMPLE_NUMBER, SAMPLE_TYPE, STORAGE_ADDRESS, EXTERNAL_NUMBER
FROM
SYSTMX2.TM2_SAMPLES
And what I need is to add two more columns with the results of some .Net syntax code so I would end up with something like"
SELECT
SAMPLE_NUMBER, SAMPLE_TYPE, STORAGE_ADDRESS, EXTERNAL_NUMBER,
SomeCodearound(STORAGE_ADDRESS) as RowPosition,
SomeCodearound(STORAGE_ADDRESS) as ColumnPosition
FROM
SYSTMX2.TM2_SAMPLES
The row and column positions are based upon where they would fall in a 9 x 9 Grid. 9 numbers for columns across the top and 9 numbers as rows down the side. This would be a lab specimen box that would hold 81 vials. Every vial has a number from 1 to 81 and is the last three characters of the STORAGE_ADDRESS value similar to FR2-S01-R01-001 or FR2-S01-R01-081. Vial number 1 would be in column 1 and row 1; vial 81 in row 9 column 9. My .net code for the row is to take the last three character of the STORAGE_ADDRESS and test with decimal.
TryParse(STORAGE_ADDRESS.Substring(STORAGE_ADDRESS.Length - 3), value)
and then take that value and convert with:
CInt(Decimal.Truncate(((value+ 9 - 1) / 9))).
And the column code is:
(value + 9) - (CInt(Decimal.Truncate(((value + 9 - 1) / 9))) * 9).
I need to make it into an inline Select statement that I can call from a web service to the Oracle server, I do not have any way to create anything database side. Right now what I do is to call the result recordset add a couple of columns and loop the results and add the values. I know there has to be a better way.
This Oracle query returns row and column positions:
select storage_address,
floor((to_number(substr(storage_address, 13, 3))-1)/9)+1 rowposition,
mod(to_number(substr(storage_address, 13, 3))-1, 9)+1 colpostion
from t
Here is SQLFiddle with test values.