Informix select for update with order by - sql

In ESQL/C I need to do a statement like this (I know that "order by" clause is not allowed in a "select ... for update")
SELECT FIRST 1 fd_foglio, fd_box_cod_soc, NVL(fd_id_subfoglio, 0)
FROM informix.foglioddt
WHERE
fd_box_data_all = TO_CHAR(CURRENT YEAR TO DAY, '%iy%m%d') AND
fd_box_cod_soc = '*'
ORDER BY fd_foglio DESC;
EXEC SQL
OPEN ifx_cursor_foglioddt_for_pending_crates;
EXEC SQL
FETCH ifx_cursor_foglioddt_for_pending_crates
INTO :fd_foglio, :fd_box_cod_soc, :local_fd_id_subfoglio;
in which I need an "order by" clause inside a "select for update" — I know that isn't allowed — in order to use a cursor successively to increment the field fd_id_subfogli (after I have used it in another part of the code) with a statement like:
UPDATE informix.foglioddt SET fd_id_subfoglio = :new_value WHERE
CURRENT OF ifx_cursor_foglioddt_for_pending_crates
How can I rewrite the previous code in order to select only the first ordered record and update a field in it?

The necessary information isn't in the question, but making a semi-educated guess, the foglioddt table has a UNIQUE constraint on columns fd_foglio and fd_id_subfoglio, but the latter can be NULL (in which case, Informix only allows one row in the table with a given fd_foglio value and a NULL fd_id_subfoglio).
Assuming that if there are any rows for a given fd_foglio value where the fd_id_subfoglio value is not null, then all the rows for that given fd_foglio value have a non-null value for the fd_id_subfoglio value, you could rephrase the UPDATE as:
UPDATE informix.foglioddt SET fd_id_subfoglio = :new_value
WHERE fd_foglio = :fd_foglio
AND (fd_id_subfoglio = :local_fd_id_subfoglio OR
fd_id_subfoglio IS NULL)
The first part of the OR condition deals with the normal case, where you've got both values that make up the primary key, and you simply change the identified row. The second part of the OR condition deals with the case where the only row in the table has fd_id_subfoglio set null.
Clearly, if my interpretation of the data structure is incorrect, then this won't work. The solution will then be to select the values from the primary key columns so that you can update using the primary key to identify the row to be updated — that's why primary keys are important! They're the way you can address individual rows by a set of values. Indexes on primary keys are also important to speed up such access (and to enforce uniqueness).

Related

Can't update only 1 row. How to change 1 row?

I found out that Oracle does not support LIMIT clause unlike to MySQL and to update only 1 row in table i've tried this:
UPDATE "Schedule"
SET "Position" = 'Manager'
WHERE "Position" IN
(SELECT "Position"
FROM "Schedule"
WHERE "Position"='Tester'
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY);
And 3 rows are updated.
When i run this:
SELECT "Position"
FROM "Schedule"
WHERE "Position"='Tester'
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY;
i get only one row (as it should be).
But updating 1 row doesn't work as i mentioned before. Are there any other methods to update only 1 specific row?
You need just a slight, provided that your DB's version is at least 12c, change by replacing the part WHERE Position IN (SELECT Position with WHERE ID IN (SELECT ID as having a primary column. The current case has no sense, since all testers will be converted to managers even replacing the IN <subquery> with ='Tester'. So use the following update statement by removing double quotes
UPDATE Schedule
SET Position = 'Manager'
WHERE ID IN
(SELECT ID
FROM Schedule
WHERE Position = 'Tester'
FETCH NEXT 1 ROW ONLY);
Demo
Ideally your "Schedule" table would have a primary key and you'd use that to perform your update. Assuming that "Schedule" doesn't have a primary key you can use ROWID:
UPDATE "Schedule"
SET "Position" = 'Manager'
WHERE ROWID IN (SELECT ROWID
FROM "Schedule"
WHERE "Position" = 'Tester'
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY);
Note that using ROWID like this is very poor practice, and all tables should have a primary key specified when they're created so that individual rows can be found reliably. For a column or set of columns to be a valid primary key they must be A) non-NULL, B) unique, and C) unchanging.
Also - I strongly suggest that you get in the habit of using names for tables and columns which do not require that they be quoted. Quoted identifiers are an opportunity for syntax errors that do nothing to make your life better. If you create a table named SCHEDULE you can still refer to is as Schedule, ScHeDuLe, schedule, or whatever other combination of upper and lower case letters you care to use in your code.

Assign a value if the field is not null

I'm working on a SQL project on MS Access.
I would like to know if there is a way to assign a same value everytime a field is NOT NULL. I know that there is the Nz() function which does the opposite, but I don't know other functions.
Also, I would like to put a different value everytime the field is NULL
My table looks like that.
date
MARCH17
JUNE18
JULY19
and I would like to get something like that.
date
1
2
PRESENT
PRESENT
5
PRESENT
If I have to create another column, it's perfectly fine too.
Thanks in advance !
You will need to place your new information in a new column, otherwise, if you run the query more than once, you will get PRESENT for everything since the first query replaces the NULL date with a sequence number.
If you have an id column you can use:
UPDATE table SET new_column = (SELECT IIF(date IS NULL, id,'PRESENT'))
If you don't have an id column (which is strongly recommended) then you'll need to generate a sequence number.
Does your table have a Primary Key? Then you want to count all nulls where primary key is less than this one to give you your number, and put present where it isn't null. So (assuming your field is F1 and Primary Key is called PK) the following calculated field
=IIf(ISNULL([F1]),DCOUNT("[PK]","MYTABLE","[PK]<" & [PK]) &""","PRESENT")
You can use:
UPDATE
YourTable
SET
[date] = IIf([date] Is Null,
(Select Count(*) + 1 From YourTable As T Where T.[date] <> 'PRESENT'),
'PRESENT'))
WHERE
[date] <> 'PRESENT'

How to generate auto generated sequence no in sql

I have one requirement. I already have a table named WorkOrder. In this table there is a column Named WorkorderId set as primary key and identity. The next one is voucherNumber. Now I want to generate voucherNumber automatically. The condition is voucher number will not repeat. E.g., first I insert 2 rows into the table and after that I delete the 2nd entry. The next time my voucher number should be 3. Again i insert 3 more entries then after that my voucher no should be 6. Then i delete one row from this table after that my voucher number should be 7. If i delete the last row (I mean 7) then next time the voucher number should the same.
Use IDENTITY(...) when creating the column. This will make a field auto-increment its value.
You'll have to drop the column first in case that it already exists. There is no (clean) way to make this happen on already existing columns.
For further information and examples you can check out http://www.w3schools.com/sql/sql_autoincrement.asp
Edit: Sorry, I have overlooked the info that you are already using IDENTITY(...) on the PK column. Unfortunately SQL-Server can only have a single column with the IDENTITY property per table... So in this case you'll have to make use of a trigger.
This is an example:
CREATE TRIGGER CountRows
ON TestCount
AFTER UPDATE
AS
UPDATE TestCount SET Cnt = Cnt +1 WHERE ID IN (SELECT ID from inserted)
GO
In case you want to enter an IDENTIFIER to the record, it is best to use uniqueIdentifier type column. It is a string constant in the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, in which each x is a hexadecimal digit in the range 0-9 or a-f. For example, 6F9619FF-8B86-D011-B42D-00C04FC964FF is a valid uniqueidentifier value.
On insertion, you can simply proceed as follows;
Insert into MyTable
(WorkorderId, WorkName) values (NewId(), 'Test')
Using this, you can be sure the Id is globally unique.

Get values based on newly inserted value using SQL

I want to make filtration on a column after selecting a specific value of another column in the same table, I tried to use #... special character followed by the column's name to get the address of this value.
My SQL statement is like the following :
SELECT ATTRIBUTE FROM TABLE WHERE FIELD = '#FIELDNAME';
If I used a specific value instead of #FIELDNAME, it will work properly but it will be static but I need it to be dynamic based on the selected value.
Create another table which will have the list of values that are in the FIELDNAME and give each record a unique id ,then retrieve the value depending on what you have selected by the name of the new table's field preceded by '#...'
I don't know if that what are you looking for, please let me know.
If no triggers are allowed, do you have any date/time column in the table? Is it possible to have that extra column anyway to see the time of a newly inserted row?
You may have to check the lastest row entered, save its field value into a variable. Then do the select based on the variable value.
Based on the vague last row id you could try the following (it's not pretty). But again, if you have date/time that's more accurate.
select attribute from table
where field = (select field from table
where rowid =(select max(rowid) from table))
;
upate
Do you have the priviledge to set up your insert command as below:
insert into table (id, col1, col2,...) values (1,'something', 'something',...)
returning id into variable; -- you may either save field or id depending on your table
Then you may use this variable to select the records you want.

Intervals: How can I make sure there is just one row with a null value in a timstamp column in table?

I have a table with a column which contains a 'valid until' Date and I want to make sure that this can only be set to null in a single row within the table. Is there an easy way to do this?
My table looks like this (postgres):
CREATE TABLE 123.myTable(
some_id integer NOT NULL,
valid_from timestamp without time zone NOT NULL DEFAULT now(),
valid_until timestamp without time zone,
someString character varying)
some_id and valid_from is my PK. I want nobody to enter a line with a null value in column valid_until if there is already a line with null for this PK.
Thank you
In PostgreSQL, you have two basic approaches.
Use 'infinity' instead of null. Then your unique constraint works as expected. Or if you cannot do that:
CREATE UNIQUE INDEX null_valid_from ON mytable(someid) where valid_until IS NULL
I have used both approaches. I find usually the first approach is cleaner and it allows you to use range types and exclude constraints in newer versions of PostgreSQL better (to ensure no two time ranges overlap based on a given given someid), bt the second approach often is useful where the first cannot be done.
Depending on the database, you can't have null in a primary key (I don't know about all databases, but in sql server you can't). The easiest way around this I can think of is to set the date time to the minimum value, and then add a unique constraint on it, or set it to be the primary key.
I suppose another way would be to set up a trigger to check the other values in the table to see if another entry is null, and if there is one, don't allow the insert.
As Kevin said in his answer, you can set up a database trigger to stop someone from inserting more than one row where the valid until date is NULL.
The SQL statement that checks for this condition is:
SELECT COUNT(*)
FROM TABLE
WHERE valid until IS NULL;
If the count is not equal to 1, then your table has a problem.
The process that adds a row to this table has to perform the following:
Find the row where the valid until value is NULL
Update the valid until value to the current date, or some other meaningful date
Insert the new row with the valid until value set to NULL
I'm assuming you are Storing Effective-dated-records and are also using a valid from date.
If so, You could use CRUD stored procedures to enforce this compliance. E.G the insert closes off any null valid dates before inserting a new record with a null valid date.
You probably need other stored procedure validation to avoid overlapping records and to allow deleting and editing records. It may be more efficient (in terms of where clauses / faster queries) to use a date far in the future rather than using null.
I know only Oracle in sufficient detail, but the same might work in other databases:
create another column which always contains a fixed value (say '0') include this column in your unique key.
Don't use NULL but a specific very high or low value. I many cases this is actually easier to use then a NULL value
Make a function based unique key on a function converting the date including the null value to some other value (e.g. a string representation for dates and 'x' for null)
make a materialized view which gets updated on every change on your main table and put a constraint on that view.
select count(*) cnt from table where valid_until is NULL
might work as the select statement. And a check constraint limiting the cnt value to the values 0 and 1
I would suggest inserting to that table through an SP and putting your constraint in there, as triggers are quite hidden and will likely be forgotten about. If that's not an option, the following trigger will work:
CREATE TABLE dbo.TESTTRIGGER
(
YourDate Date NULL
)
CREATE TRIGGER DupNullDates
ON dbo.TESTTRIGGER
FOR INSERT, UPDATE
AS
DECLARE #nullCount int
SELECT #nullCount = (SELECT COUNT(*) FROM TESTTRIGGER WHERE YourDate IS NULL)
IF(#NullCount > 1)
BEGIN
RAISERROR('Cannot have Multiple Nulls', 16, 1)
ROLLBACK TRAN
END
GO
Well if you use MS SQL you can just add a unique Index on that column. That will allow only one NULL. I guess that if you use other RDBMS, this will still function.