Checking the row existence before insertion using Cassandra within a Go application - go

I am using gocql with my Go application and trying to solve the issue described below.
CREATE TABLE IF NOT EXISTS website.users (
id uuid,
email_address text,
first_name text,
last_name text,
created_at timestamp,
PRIMARY KEY (email_address)
);
This query is going to override matching record which is Cassandra's expected behaviour.
INSERT INTO users (id, email_address, first_name, last_name, created_at)
VALUES (?, ?, ?, ?, ?)
In order to prevent overriding the existing record we can use IF NOT EXISTS at the end of the query.
INSERT INTO users (id, email_address, first_name, last_name, created_at)
VALUES (?, ?, ?, ?, ?)
IF NOT EXISTS
However, there is no way for me to know if the query affected any rows in DB or not. Somehow I need to return something like "Record exists" message back to caller but it is currently not possible. If there was something specific with session.Query(...).Exec() it would be useful but there isn't as far as I know.
I was thinking to SELECT by email_address before proceeding with INSERT if there was no matching record but as you can guess this is not feasible because by the time I INSERTed a new record after SELECT, some other operation could have INSERTed a new record with same email address.
How do we handle such scenario?

The solution is to use ScanCAS and the test case example from the library is here.
NOTE:
Order of the fields in ScanCAS() should match cqlsh> DESCRIBE keyspace.users; output for the CREATE TABLE ... block.
If you don't care about the scanned fields, prefer MapScanCAS instead.
func (r Repository) Insert(ctx context.Context, user User) error {
var (
emailAddressCAS, firstNameCAS, idCAS, lastNameCAS string
createdAtCAS time.Time
)
query := `
INSERT INTO users (email_address, created_at, first_name, id, last_name)
VALUES (?, ?, ?, ?, ?) IF NOT EXISTS
`
applied, err := r.session.Query(
query,
user.EmailAddress,
user.CreatedAt,
user.FirstName,
user.LastName,
user.CreateAt,
).
WithContext(ctx).
ScanCAS(&emailAddressCAS, &createdAtCAS, &firstNameCAS, &idCAS, &lastNameCAS)
if err != nil {
return err
}
if !applied {
// Check CAS vars here if you want.
return // your custom error implying a duplication
}
return nil
}

If you're using INSERT with IF NOT EXISTS, then in contrast to "normal" inserts that doesn't return anything, such query returns a single row result consisting of:
field with name [applied], and true value - if there was no record before, and new row was inserted
field with name [applied], and false value + all columns of existing row.
So you just need to get result of your insert query, and analyze it. See documentation for more details.

Related

pgx is not returning rows after I do an insert

Im just doing an insert using the following code, and while i can see the data in the database, pgx is not returning any rows.
rows, err := db.Query(context.Background(), `
INSERT INTO
reservation (room_id, user_id)
VALUES
($1, $2)
`, roomId, userId)
I tried using QueryRow too but same thing, no rows are returned. Can anyone tell me where Im going wrong?

Laravel - how to handle not required/optional field in $fillable insert and update

I have table with below fields.
Only name is required and rest of them is optional
'name','no_of_color','offset_printing_rate','screen_printing_rate','positive_rate','regular_plate_rate','big_plate_rate',
My model
protected $fillable = [
'name',
'no_of_color',
'offset_printing_rate',
'screen_printing_rate',
'positive_rate',
'regular_plate_rate',
'big_plate_rate',
];
but when I don't fill optional field it returns error
SQL: insert into table_name (name, no_of_color, offset_printing_rate, screen_printing_rate, positive_rate, regular_plate_rate, big_plate_rate, updated_at, created_at) values (name, ?, ?, ?, ?, ?, ?, 2021-10-09 14:47:36, 2021-10-09 14:47:36)
MySQL always want a value to all cols so in laravel you have to set default for each col or set it nullable but for performance purpose, i recommended you to split optional in other table and connect them to main table by relation this will prevent increasing of null records so you don't have to waste your host space

How can I write a stored procedure which accepts a variable number of bind parameters

I want to modify my SELECT statement as follows
SELECT blob_data from table where id = ?
TO
SELECT blob_data from table where id IN (?, ?, ?, ?, ?)
TABLE
ID NOT NULL VARCHAR2(255)
BLOB_DATA BLOB
The problem with this approach is that when executing the loop in Java, I may have less than 5 IDs. I want to write a stored procedure+CallableStatement instead of a PreparedStatement to fire the second query. [I thought of writing 5 different stored procedures which accept 1 to 5 arguments, but I think there should be a better approach!]
How can I write a stored procedure which accepts a variable number of bind parameters? Also, how will the Java and corresponding Oracle code look?
I will be very grateful for help with this code
Use collections instead, for example:
SELECT blob_data from table where id IN (select * from table(?))
So you will need just to bind collection.
Create your own collection type and use it in your queries, for example:
create type number_table as table of number;
Just for testing purpose (not production) you can use internal sys.ku$_objnumset (table of number) or sys.odcinumberlist(varray of number)

Run SQL through JDBC, how to parameterize Table Names

If I have two tables:
CREATE TABLE Test_Persons_A
(
PersonID int,
LastName varchar(255),
FirstName varchar(255)
);
INSERT INTO Test_Persons_A (PersonID, LastName, FirstName)
VALUES (11, 'LN_A1', 'FN_A1');
INSERT INTO Test_Persons_A (PersonID, LastName, FirstName)
VALUES (12, 'LN_A2', 'FN_A2');
CREATE TABLE Test_Persons_B
(
PersonID int,
LastName varchar(255),
FirstName varchar(255)
);
INSERT INTO Test_Persons_B (PersonID, LastName, FirstName)
VALUES (21, 'LN_B1', 'FN_B1');
INSERT INTO Test_Persons_B (PersonID, LastName, FirstName)
VALUES (22, 'LN_B2', 'FN_B2');
commit;
The effect I want to achieve is equivalent to executing each the following two queries from java code through JDBC (oracle)
Block A:
select PersonID as PID, LastName as LN, FirstName as FN
from Test_Persons_A tp
where tp.LASTNAME like '%1%'
Block B:
select PersonID as PID, LastName as LN, FirstName as FN
from Test_Persons_B tp
where tp.LASTNAME like '%2%'
You can see that the only difference are:
Table Name
LASTNAME cretiria
But in my case, each of the the blocks are in fact a huge 'With...Select...' clause, and the java code (legacy, I can't change an iota) in fact reads each sql block from a .sql file as below before executing it through JDBC.
hugeQueryA.sql
hugeQueryB.sql
My question is: to avoid duplicating this huge block of code, instead, is it the best to construct a single large stored procedure (or functions)?
hugeStoredProc
put it in my DB, then construct two simple sql
simpleQueryA.sql
simpleQueryB.sql
to call the stored procedure with parameters (specified in each of the sql)?
Can I do this without having to use Dynamic SQL (since to do that I assume I will need change the content of the original huge sql files into strings and handle all the special chars - plus, would that also going to look awful?)?
More generally, when I need to 'parameterize' the table name, and can't use 'substitution variables', is dynamic SQL the only way to go?
Table names can't be parameterized, but you can use string substitution to generate the desired SQL after loading in your .sql file(s), provided the table names you're substituting in aren't based on user input.
For example, you could change:
from Test_Persons_A tp
to:
from {TableName} tp
After you load the file, use string substitution to replace "{TableName}" with the actual table name, then execute.

Fetch data from a table in oracle sql

I've two table in the database, the first one is Person and the second is Pilot. as following:
Person Table:
CREATE TABLE person(
person_id NUMBER PRIMARY KEY,
last_name VARCHAR2(30) NOT NULL,
first_name VARCHAR2(30) NOT NULL,
hire_date VARCHAR2(30) NOT NULL,
job_type CHAR NOT NULL,
job_status CHAR NOT NULL
);
/
INSERT INTO person VALUES (1000, 'Smith', 'Ryan', '04-MAY-90','F', 'I');
INSERT INTO person VALUES (1170, 'Brown', 'Dean', '01-DEC-92','P', 'A');
INSERT INTO person VALUES (2010, 'Fisher', 'Jane', '12-FEB-95','F', 'I');
INSERT INTO person VALUES (2080, 'Brewster', 'Andre', '28-JUL-98', 'F', 'A');
INSERT INTO person VALUES (3190, 'Clark', 'Dan', '04-APR-01','P', 'A');
INSERT INTO person VALUES (3500, 'Jackson', 'Tyler', '01-NOV-05', 'F', 'A');
INSERT INTO person VALUES (4000, 'Miller', 'Mary', '11-JAN-08', 'F', 'A');
INSERT INTO person VALUES (4100, 'Jackson', 'Peter', '08-AUG-11', 'P','I');
INSERT INTO person VALUES (4200, 'Smith', 'Ryan', '08-DEC-12', 'F','A');
COMMIT;
/
Pilot Table:
CREATE TABLE pilot(
person_id NUMBER PRIMARY KEY,
pilot_type VARCHAR2(100) NOT NULL,
CONSTRAINT fk_person_pilot FOREIGN KEY (person_id)
REFERENCES person(person_id)
);
/
INSERT INTO pilot VALUES (1170, 'Commercial pilot');
INSERT INTO pilot VALUES (2010, 'Airline transport pilot');
INSERT INTO pilot VALUES (3500, 'Airline transport pilot');
COMMIT;
/
I'm asked to write a pl/sql block of code that accepts the last name from the user and return the result as following:
1) if the last name is not in the table, it returns all the rows in the table.
2) if the last name is in the table, it shows all of the employee's information.
So far I'm doing well with the code, but I got stuck in the case that there are two employees with the last name. here is the cursor that I wrote:
cursor person_info is
select last_name, first_name, hire_date, job_type, job_status, nvl(pilot_type, '-----------')
from person
full outer join pilot
on person.person_id = pilot.person_id
where upper(last_name) = upper(v_last_name)
group by last_name, first_name, hire_date, job_type, job_status, pilot_type
order by last_name, first_name, hire_date asc;
Logically, there are three cases to be covered:
the first case, when the entered last name is in the table, I return all the rows in the table and that's done.
The second case when there is only one employee with the entered last name, and this case is done as well. The last case when there are more than one employee having the same last name like for example 'Jackson' or 'Smith' in this case, my program crashes and give me the error that my select into statement returns more than one row.
select person_id
into v_n
from person
where upper(last_name) = upper(v_last_name);
if v_n = 1 then
open person_info;
fetch person_info into v_last_name, v_first_name, v_hire_date, v_job_type, v_job_status, v_pilot_type;
Can someone help me in guiding me how to fetch the data correctly? I'm not allowed to create any temporary tables or views.
I'm so sorry for making the problem longer than it should, but I was trying to be as clear as possible in explaining the problem.
Thank you in advance.
if the error is
"ORA-01422 exact fetch returns more than requested number of rows" then I think your answer is here https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:981494932508
If you EXPECT the query to return more than one row, you would code:
for x in ( select * from t where ... )
loop
-- process the X record here
end loop;
Your immediate issue is that you're selecting the matching person_id into a variable, and then seeing if that specific ID is 1. You don't have an actual ID 1 anyway so that check would never match; but it is that querying matching multiple rows that gets the error, as you can't put two matching IDs into a single scalar variable.
The way you've structured it looks like you are trying to count how many matching rows there are, rather than looking for a specific ID:
select count(person_id)
into v_n
from person
where upper(last_name) = upper(v_last_name);
if v_n = 1 then
....
When you do have multiple matches then you will need to use the same mechanism to return all of those as you do when there are no matches and you return all employees. You may find the logic should be in the cursor query rather then in PL/SQL logic. It depends on the details of the assignment though, and how it expects you to return the data in both (or all three) scenarios.
It's also possible you just aren't expected to hit this problem - it isn't clear if the assignment is finding all employees, or only those that are pilots. The issue still exists in general, but with the data you show there aren't any duplicate pilot last names. If you haven't learned about this kind of error yet perhaps you're getting a bit ahead of what your tutor expects.

Resources