SQL default row values - postgresql

If I have a table containing some data such as:
ID
some_field1
0
this is a default row with default configuration
1
user configured this field
Would SELECT whatever FROM table WHERE id=? OR id=0 LIMIT 1; always return the configured row first unless it doesn't exist?
Additionally is there a way that could check if some_field1 is configured independently of other fields without writing all the default values? Say id1 has some_field1 as null but some_field2 configured to something custom, this query would return null instead of the default unless I use the DEFAULT VALUE in the schema creation (and keep that up to date with the row in the database).
I know it could be implemented in the program logic fairly easily but I was wondering if there is a one query solution in SQL itself.

If ? is null this query return whatever entity id = 0 otherwise it return entity id =?
SELECT whatever FROM table WHERE id= coalesce(?,0) LIMIT 1;

Related

Is it possible to set condition to the values of the column in postgres?

I need to set limit to the values of the column. For example, I have a column as TEST_COLUMN with INTEGER data type. I need to set the condition that the column should only accepts the values between
1-4.(it should not be more than 4 and less than 1) Is it possible in postgress?
Thanks in Advance.
I may handle it in the code level but is there is a way to do it in Database level.
You almost certainly don't mean "postgresql 9.4" in your tags - that version is VERY old.
What you are after is a "CHECK constraint".
https://www.postgresql.org/docs/15/ddl-constraints.html
You use it something like this:
CREATE TABLE mytable (
...
my_column int NOT NULL CHECK (my_column BETWEEN 1 AND 4)
...
)
For PostgreSQL at least, the check shouldn't rely on running a query, just accessing columns from the row you are inserting or updating.

Raw SQL SELECT in EF Core 3.1 on dynamic Table

My trivial problem is this: I need to select a single field from a table filtering by the Id field.
Something that in SQL I would simply write like this:
SELECT Field FROM {Table} WHERE Id = 1
The table name is variable.
How do I do this very trivial Query with EF Core 3.1 considering that the table name can vary?
I have already tried
context.Database.ExecuteSqlCommand($"SELECT TOP 1 Field FROM {table} WHERE Id={id}")
but I cannot read the result (it always returns -1)
I also tried this:
var test = context.Set<T>()
.FromSqlRaw($"SELECT Id FROM {table}")
.FirstOrDefault();
but give me an error because it wants all the fields in the select statement
Thanks for the support

DB2: invalid use of one of the following: an untyped parameter marker, the DEFAULT keyword, or a null value

A user can only modify the ST_ASSMT_NM and , CAN_DT columns in the ST_ASSMT_REF record. In our system, we keep history in the same table and we never really update a record, we just insert a new row to represent the updated record. As a result, the "active" record is the record with the greatest LAST_TS timestamp value for a VENDR_ID. To prevent the possibility of an update to columns that cannot be changed, I wrote the logical UPDATE so that it retrieves the non-changable values from the original record and copies them to the new one being created. For the fields that can be modified, I pass them as params,
INSERT INTO GSAS.ST_ASSMT_REF
(
VENDR_ID
,ST_ASSMT_NM
,ST_CD
,EFF_DT
,CAN_DT
,LAST_TS
,LAST_OPER_ID
)
SELECT
ORIG_ST_ASSMT_REF.VENDR_ID
,#ST_ASSMT_NM
,ORIG_ST_ASSMT_REF.ST_CD
,ORIG_ST_ASSMT_REF.EFF_DT
,#CAN_DT
,CURRENT TIMESTAMP
,#LAST_OPER_ID
FROM
(
SELECT
ST_ASSMT_REF_ACTIVE_V.VENDR_ID
,ST_ASSMT_REF_ACTIVE_V.ST_ASSMT_NM
,ST_ASSMT_REF_ACTIVE_V.ST_CD
,ST_ASSMT_REF_ACTIVE_V.EFF_DT
,ST_ASSMT_REF_ACTIVE_V.CAN_DT
,CURRENT TIMESTAMP
,ST_ASSMT_REF_ACTIVE_V.LAST_OPER_ID
FROM
G2YF.ST_ASSMT_REF_ACTIVE_V ST_ASSMT_REF_ACTIVE_V --The view of only the most recent, active records
WHERE
ST_ASSMT_REF_ACTIVE_V.VENDR_ID = #VENDR_ID
) ORIG_ST_ASSMT_REF;
However, I am getting this error:
DB2 SP
:
ERROR [42610] [IBM][DB2] SQL0418N The statement was not processed because the statement contains an invalid use of one of the following: an untyped parameter marker, the DEFAULT keyword, or a null value.
It appears as though DB2 will not allow me to use a variable in a SELECT statement. For example, when I do this in TOAD for DB2:
select 1, #vendorId from SYSIBM.SYSDUMMY1
I get a popup dialog box. When I provide any string value, I get the same error.
I usually use SQL Server and I'm pretty sure I wouldn't have an issue doing this but I am not sure how to handle it get.
Suggestions? I know that I could do this in two seperate commands, 1 query SELECT to retreive the original VALUES and then supply the returned values and the modified ones to the INSERT command, but I should be able to do thios in one. Why can't I?
As you mentioned in your comment, DB2 is really picky about data types, and it wants you to cast your variables into the right data types. Even if you are passing in NULLs, sometimes DB2 wants you to cast the NULL to the data type of the target column.
Here is another answer I have on the topic.

Multiple calls extracting items from shared Queue in PostgreSQL

If there's a queue of work todo in a table that is going to be periodically polled by a number of different worker clients...what's the best way to prevent each worker from getting the same item to work on?
Say a table like: ItemId, LastAttemptDateTime, AttemptCount, and various item details.
Given an index on LastAttemptDateTime and sorted in ascending order and various clients are querying the table to grab an item to be worked on.
I use a stored procedure in MS SQL to do this...something like:
CREATE PROCEDURE GetNextQueueItem AS
SET NOCOUNT ON
DECLARE #ItemId INT
UPDATE myqueue SET #ItemId=ItemId, AttemptCount=AttemptCount+1, LastAttemptDateTime=GetDate()
WHERE ItemId=(SELECT TOP 1 ItemId
FROM myqueue
ORDER BY LastAttemptDateTime ASC)
SELECT ItemId, AttemptCount, and various item detail fields
FROM myqueue
WHERE ItemId = #ItemId
I'm fairly new to PostgreSQL and was wondering if there's alternate approaches available. (The TOP 1 will change to LIMIT 1.)
PostgreSQL equivalent could look like this:
CREATE OR REPLACE FUNCTION get_next_queue_item()
RETURNS SETOF myqueue AS
$BODY$
BEGIN
RETURN QUERY
UPDATE myqueue
SET attempt_count = attempt_count + 1
,last_attempt_ts = now()
WHERE item_id = (
SELECT item_id
FROM myqueue
ORDER BY last_attempt_ts
LIMIT 1
)
RETURNING myqueue.*;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
Major points
You only need 1 statement to do it all. UPDATE can return the updated row in the same command with the RETURNING clause.
State of the row is post-update. There is ways to get the pre-update state if needed.
No need for any variables.
I changed all identifiers to lower case, which is the cleanest style in PostgreSQL.
I renamed your column LastAttemptDateTime to last_attempt_ts
ts .. for "timestamp", because that's the name of the timestamp / datetime type in Postgres.
As you mentioned yourself, LIMIT 1 instead of TOP 1.
I use RETURNS SETOF myqueue as return type.
myqueue is the associated row-type of the table myqueue - for every table or view a row-type of the same name is automatically created in PostgreSQL.
This declaration allows for multiple rows to be returned, but LIMIT 1 guarantees that it will only ever be one.
This return type allows for RETURN QUERY to return the resulting row directly without any intermediate step. Fast, clean.
Actually, you don't need a plpgsql function at all. You can do it with a simple SQL statement:
UPDATE myqueue
SET attempt_count = attempt_count + 1
,last_attempt_ts = now()
WHERE item_id = (
SELECT item_id
FROM myqueue
ORDER BY last_attempt_ts
LIMIT 1
)
RETURNING myqueue.*;
Since PostgreSQL has sequences separate to identity columns incremented with them that can be used for other things, one nice way to do have a sequence used to set an id on the table, and another for getting the item:
Look at the currval of the sequence, if it's higher than or equal to the max id of the table, there's no items waiting.
Obtain nextval. If there is no item with a matching id then loop back to 1 (this can happen if an insert to the table failed).
Obtain the row with the matching id.
This isn't the only way to skin this cat (and not the way I've used with other databases), but has the advantage of being light on writes to the database (altering only the sequence, not the table.

Sequence Generators in T-SQL

We have an Oracle application that uses a standard pattern to populate surrogate keys. We have a series of extrinsic rows (that have specific values for the surrogate keys) and other rows that have intrinsic values.
We use the following Oracle trigger snippet to determine what to do with the Surrogate key on insert:
IF :NEW.SurrogateKey IS NULL THEN
SELECT SurrogateKey_SEQ.NEXTVAL INTO :NEW.SurrogateKey FROM DUAL;
END IF;
If the supplied surrogate key is null then get a value from the nominated sequence, else pass the supplied surrogate key through to the row.
I can't seem to find an easy way to do this is T-SQL. There are all sorts of approaches, but none of which use the notion of a sequence generator like Oracle and other SQL-92 compliant DBs do.
Anybody know of a really efficient way to do this in SQL Server T-SQL? By the way, we're using SQL Server 2008 if that's any help.
You may want to look at IDENTITY. This gives you a column for which the value will be determined when you insert the row.
This may mean that you have to insert the row, and determine the value afterwards, using SCOPE_IDENTITY().
There is also an article on simulating Oracle Sequences in SQL Server here: http://www.sqlmag.com/Articles/ArticleID/46900/46900.html?Ad=1
Identity is one approach, although it will generate unique identifiers at a per table level.
Another approach is to use unique identifiers, in particualr using NewSequantialID() that ensues the generated id is always bigger than the last. The problem with this approach is you are no longer dealing with integers.
The closest way to emulate the oracle method is to have a separate table with a counter field, and then write a user defined function that queries this field, increments it, and returns the value.
Here is a way to do it using a table to store your last sequence number. The stored proc is very simple, most of the stuff in there is because I'm lazy and don't like surprises should I forget something so...here it is:
----- Create the sequence value table.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[SequenceTbl]
(
[CurrentValue] [bigint]
) ON [PRIMARY]
GO
-----------------Create the stored procedure
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE procedure [dbo].[sp_NextInSequence](#SkipCount BigInt = 1)
AS
BEGIN
BEGIN TRANSACTION
DECLARE #NextInSequence BigInt;
IF NOT EXISTS
(
SELECT
CurrentValue
FROM
SequenceTbl
)
INSERT INTO SequenceTbl (CurrentValue) VALUES (0);
SELECT TOP 1
#NextInSequence = ISNULL(CurrentValue, 0) + 1
FROM
SequenceTbl WITH (HoldLock);
UPDATE SequenceTbl WITH (UPDLOCK)
SET CurrentValue = #NextInSequence + (#SkipCount - 1);
COMMIT TRANSACTION
RETURN #NextInSequence
END;
GO
--------Use the stored procedure in Sql Manager to retrive a test value.
declare #NextInSequence BigInt
exec #NextInSequence = sp_NextInSequence;
--exec #NextInSequence = sp_NextInSequence <skipcount>;
select NextInSequence = #NextInSequence;
-----Show the current table value.
select * from SequenceTbl;
The astute will notice that there is a parameter (optional) for the stored proc. This is to allow the caller to reserve a block of ID's in the instance that the caller has more than one record that needs a unique id - using the SkipCount, the caller need make only a single call for however many IDs are needed.
The entire "IF EXISTS...INSERT INTO..." block can be removed if you remember to insert a record when the table is created. If you also remember to insert that record with a value (your seed value - a number which will never be used as an ID), you can also remove the ISNULL(...) portion of the select and just use CurrentValue + 1.
Now, before anyone makes a comment, please note that I am a software engineer, not a dba! So, any constructive criticism concerning the use of "Top 1", "With (HoldLock)" and "With (UPDLock)" is welcome. I don't know how well this will scale but this works OK for me so far...