Check if entity already exists in a table - python

I want to check if entity already exists in a table, I tried to search google from this and I found this , but it didn't help me.
I want to return False if the entity already exists but it always insert the user.
def insert_admin(admin_name) -> Union[bool, None]:
cursor.execute(f"SELECT name FROM admin WHERE name='{admin_name}'")
print(cursor.fetchall()) # always return empty list []
if cursor.fetchone():
return False
cursor.execute(f"INSERT INTO admin VALUES('{admin_name}')") # insert the name
def current_admins() -> list:
print(cursor.execute('SELECT * FROM admin').fetchall()) # [('myname',)]
When I run the program again, I can still see that print(cursor.fetchall()) return empty list. Why is this happening if I already insert one name into the table, and how can I check if the name already exists ?

If you want to avoid duplicate names in the table, then let the database do the work -- define a unique constraint or index:
ALTER TABLE admin ADD CONSTRAINT unq_admin_name UNIQUE (name);
You can attempt to insert the same name multiple times. But it will only work once, returning an error on subsequent attempts.
Note that this is also much, much better than attempting to do this at the application level. In particular, the different threads could still insert the same name at (roughly) the same time -- because they run the first query, see the name is not there and then insert the same row.
When the database validates the data integrity, you don't have to worry about such race conditions.

Related

Not adding the same record checking loop

When adding a record to the database, how can I create a loop that will check if the record we have added exists before, check the next record if it is not, or add it if not?
I have a table in the database, there is a link structure from that table, I want this operation for that link column. Because a link can be pulled several times. There is an id in the link, I thought the id could be compared as well. The complete link is also comparable. I would be glad if you help.
I can add to this part, I am sharing the codes for an idea.
if (link == ""):
control = "false"
else:
control = "true"
if control == "true":
mySql_insert_query = "INSERT INTO ad_l(id,count,clist_id,brand_model,ad,created_at,updated_at,status) VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s)"
val = (id, count, clist_id, brand_model, ad_link, link, created_at, updated_at, status)
#cursor = scrap_db.cursor()
cursor.execute(mySql_insert_query, val) # cursor.executemany(mySql_insert_query, tuple_of_tuples)
scrap_db.commit()
print(cursor.rowcount, "Record inserted successfully into *ad_l* table")
I added something like this but it didn't work.
if exists ("SELECT ad FROM ad_l WHERE AD = ad_link"):
print('There is the same link, it is not added')
pass
else:
print('New record being added')
IMO this would be better to solve on the DB level as unique column or unique constraint (unique over multiple columns).
The DBs are optimized to do this check for you which results in better performance.
In some cases you might use a DB trigger to automatically user insert or update based on whether the record already exists or not.
In python you can use SQLAlchemy but tbh it is not very begineer friendly.
If you need to do this from other perspective and check the DB for record before you initiate some link processing (scrapping), You were heading in right direction to check for existing record, but problem is, that ad_link in your query was meant to be a variable but you pass it as part of the query.
Change it to for example:
exists (f"SELECT ad FROM ad_l WHERE AD = {ad_link}")
add the ' around {ad_link} if needed (I currently don't remember if its required)

What does return SELECT when couldn't find the value it's searching for (in SQL)?

I'm doing an exercise where I need to update some values in a table of SQL DB via python and I can't find out what SELECT return if "user" AND another "X-condition" are NOT found in the database.
I read in another thread SELECT should return an empty set, but I still got a problem with it!
When I run:
example = db.execute("SELECT value FROM table1 WHERE user=:user AND X=:Y", user=user, X=Y)
and I try with a condition like
if example == {}:
db.execute("INSERT [...]")
I never go inside this condition to do the INSERT stuff when the set is empty.
I found another route to solve this (write below), but is it valid at all?
if not example:
do the job
EDIT: I'm using sqlite3!
Assuming you're using sqlite3, the execute method always returns a Cursor which you can use to fetch the result rows, if there were any. It doesn't matter what kind of query you were doing.
If the first result you fetch is None right away, there weren't any rows returned:
if example.fetchone() is None:
db.execute("INSERT [...]")
Alternatively, you could fetch all rows as a list, and compare that against the empty list:
if example.fetchall() == []:
db.execute("INSERT [...]")

pyodbc join tables with equal named columns ("upsert") [duplicate]

I need to write an SQL query for MS-Access 2000 so that a row is updated if it exists, but inserted if it does not. (I believe this is called an "upsert")
i.e.
If row exists...
UPDATE Table1 SET (...) WHERE Column1='SomeValue'
If it does not exist...
INSERT INTO Table1 VALUES (...)
Can this be done in one query?
You can simulate an upsert in an Access by using an UPDATE query with a LEFT JOIN.
update b
left join a on b.id=a.id
set a.f1=b.f1
, a.f2=b.f2
, a.f3=b.f3
Assuming a unique index on Column1, you can use a DCount expression to determine whether you have zero or one row with Column1 = 'SomeValue'. Then INSERT or UPDATE based on that count.
If DCount("*", "Table1", "Column1 = 'SomeValue'") = 0 Then
Debug.Print "do INSERT"
Else
Debug.Print "do UPDATE"
End If
I prefer this approach to first attempting an INSERT, trapping the 3022 key violation error, and doing an UPDATE in response to the error. However I can't claim huge benefits from my approach. If your table includes an autonumber field, avoiding a failed INSERT would stop you from expending the next autonumber value needlessly. I can also avoid building an INSERT string when it's not needed. The Access Cookbook told me string concatenation is a moderately expensive operation in VBA, so I look for opportunities to avoid building strings unless they're actually needed. This approach will also avoid creating a lock for an unneeded INSERT.
However, none of those reasons may be very compelling for you. And in all honesty I think my preference in this case may be about what "feels right" to me. I agree with this comment by #David-W-Fenton to a previous Stack Overflow question: "It's better to write your SQL so you don't attempt to append values that already exist -- i.e., prevent the error from happening in the first place rather than depending on the database engine to save you from yourself."
An "upsert" is possible, if the tables have a unique key.
This old tip from Smart Access is one of my favourites:
Update and Append Records with One Query
By Alan Biggs
Did you know that you can use an update query in Access to both update
and add records at the same time? This is useful if you have two
versions of a table, tblOld and tblNew, and you want to integrate the
changes from tblNew into tblOld.
Follow these steps:
Create an update query and add the two tables. Join the two tables by
dragging the key field of tblNew onto the matching field of tblOld.
Double-click on the relationship and choose the join option that includes all records from tblNew and only those that match from
tblOld.
Select all the fields from tblOld and drag them onto the QBE grid.
For each field, in the Update To cell type in tblNew.FieldName, where FieldName matches the field name of tblOld.
Select Query Properties from the View menu and change Unique Records to False. (This switches off the DISTINCTROW option in the SQL
view. If you leave this on you'll get only one blank record in your
results, but you want one blank record for each new record to be added
to tblOld.)
Run the query and you'll see the changes to tblNew are now in tblOld.
This will only add records to tblOld that have been added to tblNew.
Records in tblOld that aren't present in tblNew will still remain in
tblOld.
I usually run the insert statement first and then I check to see if error 3022 occurred, which indicates the row already exists. So something like this:
On Error Resume Next
CurrentDb.Execute "INSERT INTO Table1 (Fields) VALUES (Data)", dbFailOnError
If Err.Number = 3022 Then
Err.Clear
CurrentDb.Execute "UPDATE Table1 SET (Fields = Values) WHERE Column1 = 'SomeValue'", dbFailOnError
ElseIf Err.Number <> 0 Then
'Handle the error here
Err.Clear
End If
Edit1:
I want to mention that what I've posted here is a very common solution but you should be aware that planning on errors and using them as part of the normal flow of your program is generally considered a bad idea, especially if there are other ways of achieving the same results. Thanks to RolandTumble for pointing this out.
You don't need to catch the error. Instead, just run the INSERT statement and then check
CurrentDb.RecordsAffected
It will either be 1 or 0, depending.
Note: It's not good practice to execute against CurrentDB. Better to capture the database to a local variable:
Dim db As DAO.Database
Set db = CurrentDb
db.Execute(INSERT...)
If db.RecordsAffected = 0 Then
db.Execute(UPDATE...)
End If
As others have mentioned, You can UPSERT with an UPDATE LEFT JOIN using the new table as the left hand side. This will add all missing records and update matching records, leaving deleted records intact.
If we follow the Create and run an update query Article we will end up with SQL that looks like this:
UPDATE Table1
INNER JOIN NewTable1 ON Table1.ID = NewTable1.ID
SET Table1.FirstName = [NewTable1].[FirstName]
but an inner join will only update matching records, it won't add new records. So let's change that INNER to a LEFT:
UPDATE Table1
LEFT JOIN NewTable1 ON Table1.ID = NewTable1.ID
SET Table1.FirstName = [NewTable1].[FirstName]
Now save a copy of the DB. Run a test on the copy before you run this on your primary DB.

Returning primary key on INSERT with pyodbc

I have a program inserting a bunch of data into an SQL database. The data consists of Reports, each having a number of Tags.
A Tag has a field report_id, which is a reference to the primary key of the relevant Report.
Now, each time I insert the data, there can be 200 Reports or even more, each maybe having 400 Tags. So in pseudo-code I'm now doing this:
for report in reports:
cursor_report = sql('INSERT report...')
cursor_report.commit()
report_id = sql('SELECT ##IDENTITY')
for tag in report:
cursor_tag += sql('INSERT tag, report_id=report_id')
cursor_tag.commit()
I don't like this for a couple of reasons. Mostly i don't like the SELECT ##IDENTITY statement.
Wouldn't this mean that if another process were inserting data at the right moment then the statement would return the wrong primary key?
I would rather like the INSERT report... to return the inserted primary key, is that possible?
Since I currently have to commit between reports the program "pauses" during these moments. If I could commit everything at the end then it would greatly reduce the time spent. I have been considering creating a seperate field in Report used for identification so I could report_id = (SELECT id FROM reports WHERE seperate_field=?) or something in the Tags, but that doesn't seem very elegant.
Wouldn't this mean that if another process were inserting data at the right moment then the ["SELECT ##IDENTITY"] statement would return the wrong primary key?
No. The database engine keeps track of the last identity value inserted for each connection and returns the appropriate value for the connection on which the SELECT ##IDENTITY statement is executed.

lastrowid() alternative or syntax without using execute in sqlite python?

In sqlite3 in python, I'm trying to make a program where the new row in the table to be written will be inserted next, needs to be printed out. But I just read the documentation here that an INSERT should be used in execute() statement. Problem is that the program I'm making asks the user for his/her information and the primary key ID will be assigned for the member as his/her ID number must be displayed. So in other words, the execute("INSERT") statement must not be executed first as the ID Keys would be wrong for the assignment of the member.
I first thought that lastrowid can be run without using execute("INSERT") but I noticed that it always gave me the value "None". Then I read the documentation in sqlite3 in python and googled alternatives to solve this problem.
I've read through google somewhere that SELECT last_insert_rowid() can be used but would it be alright to ask what is the syntax of it in python? I've tried coding it like this
NextID = con.execute("select last_insert_rowid()")
But it just gave me an cursor object output ""
I've also been thinking of just making another table where there will always only be one value. It will get the value of lastrowid of the main table whenever there is a new input of data in the main table. The value it gets will then be inserted and overwritten in another table so that every time there is a new set of data needs to be input in the main table and the next row ID is needed, it will just access the table with that one value.
Or is there an alternative and easier way of doing this?
Any help is very much appreciated bows deeply
You could guess the next ID if you would query your table before asking the user for his/her information with
SELECT MAX(ID) + 1 as NewID FROM DesiredTable.
Before inserting the new data (including the new ID), start a transaction,
only rollback if the insert failes (because another process was faster with the same operation) and ask your user again. If eveything is OK just do a commit.
Thanks for the answers and suggestions posted everyone but I ended up doing something like this:
#only to get the value of NextID to display
TempNick = "ThisIsADummyNickToBeDeleted"
cur.execute("insert into Members (Nick) values (?)", (TempNick, ))
NextID = cur.lastrowid
cur.execute("delete from Members where ID = ?", (NextID, ))
So basically, in order to get the lastrowid, I ended up inserting a Dummy data then after getting the value of the lastrowid, the dummy data will be deleted.
lastrowid
This read-only attribute provides the rowid of the last modified row. It is only set if you issued an INSERT statement using the execute() method. For operations other than INSERT or when executemany() is called, lastrowid is set to None.
from https://docs.python.org/2/library/sqlite3.html

Categories