Formula for Buy 3 get 1 Free - sql

I'm trying to write PL/SQL to get how much you pay if you purchase a certain amount of items. If you purchase 3 items, you get 1 free.
Therefore, for every 4th item purchased one of them is free. That means I pay for 3 items if 4 items are "purchased." If 10 items are attained then 8 should be paid for, 2 are free.
p-b v_p
*** ***
1 1 = 1
2 2 = 2
3 3 = 3
4 3(1) = 3
5 3(1) + 1 = 4
6 3(1) + 2 = 5
7 3(1) + 3 = 6
8 3(1) + 3(1) = 6
9 3(1) + 3(1) + 1 = 7
10 3(1) + 3(1) + 2 = 8
11 3(1) + 3(1) + 3 = 9
12 3(1) + 3(1) + 3(1) = 9
I've got
trunc(p-b / 3 * 2.4);
but my values are inconsistent.
Sometimes it works with
trunc(p-b / 3 * 2.25);
Am I doing the formula incorrectly? I'm supposed to be using trunc or mod.

To calculate the amount of Payed/Free stuff:
DECLARE
nAmount NUMBER := '10';
nPay NUMBER;
nFree NUMBER;
BEGIN
nPay := nAmount - TRUNC(nAmount / 4);
nFree := TRUNC(nAmount / 4);
DBMS_OUTPUT.PUT_LINE('Pay: ' || nPay);
DBMS_OUTPUT.PUT_LINE('Free: ' || nFree);
END;
/

I would use trunc and modulo
SELECT p-b, (trunc(p-b /4)) * 3 + mod(p-b, 4)
FROM your_table
demo

We can use the below formula to attain the result.
Assume n = p-b
n - ( n - mod( n, 4 ) ) / 4
Eg:
SELECT n, n - (n - mod(n,4))/4 result
FROM (SELECT LEVEL n
FROM DUAL
CONNECT BY LEVEL <= 12)

in simple way the result is:
p-b - trunc(p-b / 4);

Related

Code if then statement by only using $ utility

How can I code this 'if' conditions in GAMS?
Set j/1*10/
S/1*6/;
Parameter
b(s,j) export this from excel
U(s,j) export from excel
M(s)/1 100,2 250,3 140,4 120,5 132/ export from excel
;
table b(s,j)
1 2 3 4 5 6 7 8 9 10
1 3 40 23 12 9 52 9 14 89 33
2 0 0 42 0 11 32 11 15 3 7
3 10 20 12 9 5 30 14 5 14 5
4 0 0 0 9 0 3 8 0 13 5
5 0 10 11 32 11 0 3 1 12 1
6 12 20 2 9 15 3 14 5 14 5
;
u(s,j)=0;
u(s,j)$(b(s,j))=1;
Variable delta(j); "binary"
After solving a model I got the value of delta ( suppose delta(1)=1, delta(5)=1). Then Set A is
A(j)$(delta.l(j)=1)=Yes; (A={1,5})
I want to calculate parameter R(s) according to the following :
If there is no j in A(j) s.t. j in u(s,j) then R(s)=M(s)
Else if there is a j in A(j) s.t. j in u(s,j) then R(s)=min{b(s,j): j in A(j) , j in u(s,j) }
Then R(1)=3, R(2)=11,R(3)=5, R(4)=120, R(5)=11,R(6)=12.
Is it possible to code this ' if then ' statement only by $ utility?
Thanks
Following on from the comments, I think this should work for you.
(Create a parameter that mimics your variable delta just for demonstration:)
parameter delta(j);
delta('1') = 1;
delta('5') = 1;
With loop and if/else:
Create parameter R(s). Then, looping over s , pick the minimum of b(s,A) across set A where b(s,A) is defined if the sum of b(s,A) is not zero (i.e. if one of the set is non-zero. Else, set R(s) equal to M(s).
Note, the loop is one solution to the issue you were having with mixed dimensions. And the $(b(s,A)) needs to be on the first argument of smin(.), not on the second argument.
parameter R(s);
loop(s,
if (sum(A, b(s,A)) ne 0,
R(s) = smin(A$b(s,A), b(s,A));
else
R(s) = M(s);
);
);
With $ command only (#Lutz in comments):
R(s)$(sum(A, b(s,A)) <> 0) = smin(A$b(s,A), b(s,A));
R(s)$(sum(A, b(s,A)) = 0) = M(s);
Gives:
---- 56 PARAMETER R
1 3.000, 2 11.000, 3 5.000, 4 120.000, 5 11.000, 6 12.000

Efficient way in SQL to compute a set of rows against all other rows?

Let's say I have a table with data that looks like this:
d user val
1 1 .94
1 2 -.88
1 3 .24
1 4 .74
2 1 .35
2 2 .68
2 3 -.98
2 4 .62
3 1 -.81
3 2 .97
3 3 .29
3 4 ___ (this row doesn't exist in the database)
4 1 .76
4 2 .38
4 3 -.98
4 4 .15
5 1 .69
5 2 .27
5 3 -.49
5 4 -.59
For a given user (let's say 2), I would like the following output:
user calc
1 -.102
3 .668
4 -.1175
Generalized:
user calc
1 ((-.88 - .94) + (.68 - .35) + (.97 - -.81) + (.38 - .76) + (.27 - .69)) / 5
3 ((-.88 - .24) + (.68 - -.98) + (.97 - .29) + (.38 - -.98) + (.27 - -.49)) / 5
4 ((-.88 - .74) + (.68 - .62) + (.38 - .15) + (.27 - -.59)) / 4
Generalized Further:
user calc
1 sum of (user2's d value - user1's d value) / count
3 sum of (user2's d value - user3's d value) / count
4 sum of (user2's d value - user4's d value) / count
To explain further, I'd like to obtain an output that shows everyone's relation to a given user (in this case user 2). In my actual dataset there are hundreds of unsorted distinct users and d values, but I've tried to simplify the dataset for this question.
Also, please note that not all user's have a d value, so it should only factor in matching sets. See how in the example above user 4 doesn't have a value for d=3, so that one is skipped in the calculation.
A join and aggregation should work:
select
t2.user, avg(t1.val - t2.val) as calc
from my_table t1
join my_table t2 on t1.d = t2.d and t1.user <> t2.user
where t1.user = 2
group by t2.user

Generating certain sequence of numbers with defined ranges

How can I use a collection of nested for loops (or any other type) to produce a sequence like this with these variables:
length is how many digts to go to
max is the maximum number
min is the minimum number
Lets say for this case:
length = 2
max = 3
min = 1
it would produce:
11
12
13
21
22
23
31
32
33
This works "ok" for only length = 1, but not really, since I still have annoying 0's at the start
For i = 1 To length
For ii = 0 To i
For iii = 1 To 5
Console.WriteLine(Str(ii) + Str(iii))
Next
Next
Next
As this looks like a homework problem, I am going to attempt to help you think through this problem without actually giving you the answer in code.
Let's think through this problem...
You have the range 1-3. So your first sequence is easy:
1, 2, 3
Now you want to produce a sequence from 11 to 13. What's the change, or difference, between 1 through 3 and 11 through 13? The answer is you've added 10.
The same is true for 21 through 23 - you've added 10 again.
So, what you want to do is iterate from 1 through 3.
Then, iterate from 1 through 3 this time adding 10.
Then, iterate from 1 through 3 this time adding 20.
Thinking about this, you are essentially doing this:
1
2
3
10 + 1
10 + 2
10 + 3
10 + 10 + 1
10 + 10 + 2
10 + 10 + 3
etc
Or, you could also think about it like this:
(0 * 10) + 1
(0 * 10) + 2
(0 * 10) + 3
(1 * 10) + 1
(1 * 10) + 2
(1 * 10) + 3
(2 * 10) + 1
(2 * 10) + 2
(2 * 10) + 3
etc
Can you see a pattern forming?

How to find Only hour value in 12hrs format using sql

I want hour value only from the currenttime
Like if it is 4:31:13 so i want only 4 and that also should be in 12hrs format..
Please help me...
SELECT
case when DATEPART(hh, time) > 12
then DATEPART(hh, time) - 12
else DATEPART(hh, time) end as hour
from time;
SAMPLE FIDDLE.
This will also correctly handle 12 AM and 12 PM time:
select case datepart(hh, getdate())
when 0 then 12
when 12 then 12
else datepart(hh, getdate()) % 12
end;
SELECT (DATEPART(HOUR, GETDATE()) + 11) % 12 + 1
At 1 am this will return:
1 + 11 = 12; 12 % 12 = 0; 0 + 1 = 1
At 1 pm (13:00 in 24 hrs format) you'll get:
13 + 11 = 24; 24 % 12 = 0; 0 + 1 = 1
Similarly, at 2 am and at 2 pm (14:00):
2 + 11 = 13; 13 % 12 = 1; 1 + 1 = 2
14 + 11 = 25; 25 % 12 = 1; 1 + 1 = 2
and so on till 11 am and 11 pm. At 12 am (0:00) & 12 pm (12:00) it will go like this:
0 + 11 = 11; 11 % 12 = 11; 11 + 1 = 12
12 + 11 = 23; 23 % 12 = 11; 11 + 1 = 12
I don't know what database engine are you using, but in PostgreSQL you can do this:
select to_char(current_timestamp, 'HH12');
SELECT SUBSTRING(LTRIM(RIGHT(CONVERT(VARCHAR(20), GETDATE(), 100), 7)),1,1)
SELECT DATEPART(hour, '2008-09-27 09:30:00.000')
Refer this

How to get bitstring length of integer in oracle pl/sql?

How I can calculate bit length of an integer in Oracle's PL/SQL?
I would like to get something like cast INT to BIT STRING and then LENGTH( LTRIM(BIT STRING, '0') )'
you can use the following formula to get the number of characters of the binary representation of an integer n (n>0):
ceil(log(2, n + 1))
SQL> SELECT n, ceil(log(2, n + 1)) num_of_char
2 FROM (SELECT ROWNUM n FROM dual CONNECT BY LEVEL <= 64);
N NUM_OF_CHAR
---------- -----------
1 1
2 2
3 2
4 3
5 3
6 3
7 3
8 4
[...]
15 4
16 5
[...]
31 5
32 6
[...]
63 6
64 7