updating a value with several conditions - sql

I am trying to add several condition. I'd like to update base2 with sum of itself with an intermediate value, and I'd like to post some conditions on intermediate value and base2.
I modified the table manually in database. Intermediatevalue is one of the columns in the table, and is calculated based on the base2 value,
In the first row, I have a base2 value, and I calculate to get first row intermediate value, now in the second row, I need to set the new base2=previous base2+previous intermediate value. That is why I have two counters to trace where the item's positions are. Counter1 counts the index of itemid, and counter2 traces counts for the loop inside the itemid
The question is how to set this new base2. Is it possible to set my new base2 on one line? Or will I have to set another variable to the intermediate value in the previous row and add it as a new variable to base2?
Here below is what I want to have, but has errors (function missing) ).
UPDATE TABLE2 SET base2=
(base2+INTERMEDIATEVALUE WHERE loadingordinal=counter2 AND itemid=counter1)
WHERE loadingordinal=counter2 +1 AND itemid=counter1

In VFP, doing such updates via Update_SQL is hard. Fortunately there are many ways to solve the problem and one is to use the xBase command instead (in xBase you can use REPLACE for SQL-UPDATE. Replace operates on "current row" by default (no scope clause is included). You didn't supply any schema nor any sample data so I will give only a plain sample by guess. Your code would look like:
local lnPrevious, lnItemId
lnItemId = -1
Select Table2
Scan
&& Save Current Id (ItemID?)
lnItemId = Table2.ItemID
&& Do calculation using previous value
&& On first row it is the same as base2
lnPrevious = base2
scan While Table2.ItemId = m.lnItemId
Replace Base2 with m.lnPrevious, ;
IntermediateValue with DoCalculation(Base2)
&& Previous value for next row
lnPrevious = Base2 + IntermediateValue
endscan
skip -1 && Endscan would move the pointer
Endscan
Note, if you need more than ItemId (or maybe passing Base2 and IntermediateValue itself rather than lnPrevious) you could as well do something like:
local loRow
scan
scatter to name loRow memo
scan while table2.ItemId = m.loRow.ItemId
...

I'm not sure what INTERMEDIATEVALUE is but if it is a table then you can follow the query below or you can just adjust it. You can do subqueries in order to achieve this type of condition
UPDATE TABLE2
SET base2 = base2 + (SELECT *
FROM INTERMEDIATEVALUE
WHERE loadingordinal=counter2 AND itemid=counter1)
WHERE loadingordinal=counter2 +1 AND itemid=counter1

Related

Add new column in SQL based on 2 conditions

I would like to create a new column in SQL using two conditions. I want to create a column that says 1 if the item count is greater than 1 and if item/sum is greater than 5, otherwise return 0. This is not my original data, is there anyway I could add this new column with these specified rows using a select statement.
First I want to say that using SQL key words or SQL function names as table name or column name should be avoided, so if possible, I recommend to rename the columns "number" and "sum".
The second point is it's unclear what should happen in case the sum column is 0. Since a division by zero is not possible, you will need to add a condition for that.
Anyway, the way to achieve such things is using CASE WHEN. So let's add the new column:
ALTER TABLE yourtable ADD column column_name INT;
Now, you need to execute an update command for that column providing the logic you want to apply. As an example, you can do this:
UPDATE yourtable SET column_name =
CASE WHEN item <= 1 THEN 0
WHEN sum = 0 THEN 1
WHEN item / sum > 0.5 THEN 1
ELSE 0 END;
This will set the new column to 1 only in case item is > 1 and sum is 0 or sum item / sum is > 0.5 (greater 50%). In all other cases it will be set to 0. Again, the bad column naming can be seen since "WHEN sum..." looks like you really build a sum and not just use a column.
If you want as example to set the new column to 0 instead of 1 when the sum is 0, just change it and try out.
In case you want to automatically apply this logic fur future inserts or updates, you can add a trigger on your new column. Something like this:
CREATE TRIGGER set_column_name
BEFORE INSERT ON yourtable
FOR EACH ROW
SET new.column_name = CASE WHEN new.item <= 1 THEN 0
WHEN new.sum = 0 THEN 1
WHEN new.item / new.sum > 0.5 THEN 1
ELSE 0 END;
But take care, the syntax of triggers depend on the DB you are using (this example will work in MYSQL). Since you did not tell us which DB you use, you maybe need to modify it. Furthermore, depending on your DB type and your requirements, you need zero, one or two triggers (for updates and for inserts).

function to sum all first value of Results SQL

I have a table with "Number", "Name" and "Result" Column. Result is a 2D text Array and I need to create a Column with the name "Average" that sum all first values of Result Array and divide by 2, can somebody help me Pls, I must use the create function for this. Its look like this:
Table1
Number
Name
Result
Average
01
Kevin
{{2.0,10},{3.0,50}}
2.5
02
Max
{{1.0,10},{4.0,30},{5.0,20}}
5.0
Average = ((2.0+3.0)/2) = 2.5
= ((1.0+4.0+5.0)/2) = 5.0
First of all: You should always avoid storing arrays in the table (or generate them in a subquery if not extremely necessary). Normalize it, it makes life much easier in nearly every single use case.
Second: You should avoid more-dimensional arrays. The are very hard to handle. See Unnest array by one level
However, in your special case you could do something like this:
demo:db<>fiddle
SELECT
number,
name,
SUM(value) FILTER (WHERE idx % 2 = 1) / 2 -- 2
FROM mytable,
unnest(avg_result) WITH ORDINALITY as elements(value, idx) -- 1
GROUP BY number, name
unnest() expands the array elements into one element per record. But this is not an one-level expand: It expand ALL elements in depth. To keep track of your elements, you could add an index using WITH ORDINALITY.
Because you have nested two-elemented arrays, the unnested data can be used as follows: You want to sum all first of two elements, which is every second (the odd ones) element. Using the FILTER clause in the aggregation helps you to aggregate only exact these elements.
However: If that's was a result of a subquery, you should think about doing the operation BEFORE array aggregation (if this is really necessary). This makes things easier.
Assumptions:
number column is Primary key.
result column is text or varchar type
Here are the steps for your requirements:
Add the column in your table using following query (you can skip this step if column is already added)
alter table table1 add column average decimal;
Update the calculated value by using below query:
update table1 t1
set average = t2.value_
from
(
select
number,
sum(t::decimal)/2 as value_
from table1
cross join lateral unnest((result::text[][])[1:999][1]) as t
group by 1
) t2
where t1.number=t2.number
Explanation: Here unnest((result::text[][])[1:999][1]) will return the first value of each child array (considering you can have up to 999 child arrays in your 2D array. You can increase or decrease it as per your requirement)
DEMO
Now you can create your function as per your requirement with above query.

Sql column value as formula in select

Can I select a column based on another column's value being listed as a formula? So I have a table, something like:
column_name formula val
one NULL 1
two NULL 2
three one + two NULL
And I want to do
SELECT
column_name,
CASE WHEN formula IS NULL
val
ELSE
(Here's where I'm confused - How do I evaluate the formula?)
END as result
FROM
table
And end up with a result set like
column_name result
one 1
two 2
three 3
You keep saying column, and column name, but you're actually talking about rows, not columns.
The problem is that you (potentially) want different formulas for each row. For example, row 4 might be (two - one) = 1 or even (three + one) = 4, where you'd have to calculate row three before you could do row 4. This means that a simple select query that parses the formulas is going to be very hard to do, and it would have to be able to handle each type of formula, and even then if the formulas reference other formulas that only makes it harder.
If you have to be able to handle functions like (two + one) * five = 15 and two + one * five = 7, then you'd be basically re-implementing a full blown eval function. You might be better to return the SQL table to another language that has eval functions built in, or you could use something like SQL Eval.net if it has to be in SQL.
Either way, though, you've still got to change "two + one" to "2 + 1" before you can do the eval with it. Because these values are in other rows, you can't see those values in the row you're looking at. To get the value for "one" you have to do something like
Select val from table where column_name = 'one'
And even then if the val is null, that means it hasn't been calculated yet, and you have to come back and try again later.
If I had to do something like this, I would create a temporary table, and load the basic table into it. Then, I'd iterate over the rows with null values, trying to replace column names with the literal values. I'd run the eval over any formulas that had no symbols anymore, setting the val for those rows. If there were still rows with no val (ie they were waiting for another row to be done first), I'd go back and iterate again. At the end, you should have a val for every row, at which point it is a simple query to get your results.
Possible solution would be like this kind....but since you mentioned very few things so this works on your above condition, not sure for anything else.
GO
SELECT
t1.column_name,
CASE WHEN t1.formula IS NULL
t1.val
ELSE
(select sum(t2.val) from table as t2 where t2.formula is not null)
END as result
FROM
table as t1
GO
If this is not working feel free to discuss it further.

Pig FILTER returns empty bag that I can't COUNT

I'm trying to count how many values in a data set match a filter condition, but I'm running into issues when the filter matches no entries.
There are a lot of columns in my data structure, but there's only three of use for this example: key - data key for the set (not unique), value - float value as recorded, nominal_value - float representing the nominal value.
Our use case right now is to find the number of values that are 10% or more below the nominal value.
I'm doing something like this:
filtered_data = FILTER data BY value <= (0.9 * nominal_value);
filtered_count = FOREACH (GROUP filtered_data BY key) GENERATE COUNT(filtered_data.value);
DUMP filtered_count;
In most cases, there are no values that fall outside of the nominal range, so filtered_data is empty (or null. Not sure how to tell which.). This results in filtered_count also being empty/null, which is not desirable.
How can I construct a statement that will return a value of 0 when filtered_data is empty/null? I've tried a couple of options that I've found online:
-- Extra parens in COUNT required to avoid syntax error
filtered_count = FOREACH (GROUP filtered_data BY key) GENERATE COUNT((filtered_data.value is null ? {} : filtered_data.value));
which results in:
Two inputs of BinCond must have compatible schemas. left hand side: #1259:bag{} right hand side: #1261:bag{#1260:tuple(cf#1038:float)}
And:
filtered_count = FOREACH (GROUP filtered_data BY key) GENERATE (filtered_data.value is null ? 0 : COUNT(filtered_data.value));
which results in an empty/null result.
The way you have it set up right now, you will lose information about any keys for which the count of bad values is 0. Instead, I'd recommend preserving all keys, so that you can see positive confirmation that the count was 0, instead of inferring it by absence. To do that, just use an indicator and then SUM that:
data2 =
FOREACH data
GENERATE
key,
((value <= 0.9*nominal_value) ? 1 : 0) AS bad;
bad_count = FOREACH (GROUP data2 BY key) GENERATE group, SUM(data2.bad);

increment SQL field manually

I'm attempting to create a SAP UDO for product registrations. The UDO automatically creates two required fields "docentry" and "code." docentry auto-increments, code does not. I don't have accessing to the underlying database structure to fix that, but I can attach a query to the code field.
When a record is created, can I setup a query that would increment based on the previous row's value for code? I'm not looking for anything useful out of this field, but we will need to both batch import .csv files on a regular basis and also take direct submissions from our site, so I'm just looking for the easiest way to make it work.
Thank you.
UPDATE table_name_here SET code = code + 1 WHERE ? = ??
Replace the ? with unique identifier column name and ?? with it's value for row you are working with.
After your comment:
Let's say you have previous row code value stored in $1 and actual row id in $2. Then:
UPDATE registrations SET code = $1 + 1 WHERE id = $2
After your second comment:
UPDATE registrations SET code = (SELECT MAX(code) FROM registrations) + 1 WHERE docentry = `new_row_docentry_value`