How to reuse bind variables in oracledb-python?

Question:

I’m trying to do an upsert with below query but I’m getting oracledb.exceptions.DatabaseError: DPY-4009: 11 positional bind values are required but 5 were provided. Meaning I’m unable to reuse my bind variables.

UPDATE Competition 
SET abbreviation = :1, descriptions = :2, levels = :3, source = :4, competitionId = :5
WHERE competitionId=:5;
IF ( sql%notfound ) THEN
    INSERT INTO Competition
    VALUES (:1, :2, :3, :4, :5);
END IF;

The query is executed in the context:

cursor.executemany(upsert_string, parsed_data)

where the upsert_string is the above query and parsed_data is a list of tuples.

How do I reuse bind variables in this context, is there any way?

Asked By: MisterButter

||

Answers:

Yes, there is. There are a few options available to you.

  1. You can use bind by name instead of bind by position. In your example it might look like this:
BEGIN
    UPDATE Competition SET
        abbreviation = :abbrev,
        descriptions = :descr,
        levels = :levels,
        source = :source
    WHERE competitionId = :compet;
    IF ( sql%notfound ) THEN
        INSERT INTO Competition
        VALUES (:abbrev, :descr, :levels, :source, :compet);
    END IF;
END;
  1. Since this is an anonymous PL/SQL block, you can also store the bind values in temporary variables, like this:
DECLARE
    t_Abbrev        varchar2(10);
    t_Description   varchar2(100);
    t_Levels        varchar2(50);
    t_Source        varchar2(100);
    t_CompetitionId number;
BEGIN
    t_Abbrev := :1;
    t_Description := :2;
    t_Levels := :3;
    t_Source := :4;
    t_CompetitionId := :5;
    UPDATE Competition SET
        abbreviation = t_Abbrev,
        descriptions = t_Description,
        levels = t_Levels,
        source = t_Source
    WHERE competitionId = t_CompetitionId;
    IF ( sql%notfound ) THEN
        INSERT INTO Competition
        VALUES (t_Abbrev, t_Description, t_Levels, t_Source, t_CompetitionId);
    END IF;
END;
  1. Finally, you can use the MERGE statement instead of an anonymous PL/SQL block:
MERGE INTO Competition C USING (
    SELECT
        :1 as ABBREV,
        :2 as DESCRIPTION,
        :3 as LEVELS,
        :4 as SOURCE,
        :5 as COMPETITION_ID
    FROM DUAL
) S
ON (C.CompetitionId = S.COMPETITION_ID)
WHEN MATCHED THEN
    UPDATE COMPETITION SET
        Abbreviation = s.ABBREV,
        Descriptions = s.DESCRIPTION,
        Levels = s.LEVELS,
        Source = s.SOURCE
WHEN NOT MATCHED THEN
    INSERT INTO Competition VALUES (
        s.ABBREV,
        s.DESCRIPTION,
        s.LEVELS,
        s.SOURCE,
        s.COMPETITIONID
    )
Answered By: Anthony Tuininga