1
votes

Comment rechercher une ou plusieurs lignes avec des données de recherche variables (certaines données, d'autres non)

J'utilise des instructions SQL pour rechercher des lignes dans ma base de données en fonction des données données. si les colonnes sont (ID, Username, Password, Clearance, Class_Count), mon programme ne recherchera parfois que le nom d'utilisateur ou l'autorisation. Mais parfois, il recherchera à la fois le nom d'utilisateur et le nombre de classes. Je ne connais aucun moyen de l'implémenter facilement dans mon code sans créer (je crois) environ 7 instructions IF différentes pour vérifier quelles données sont utilisées pour la recherche (des exemples seront donnés dans le code ci-dessous)

sqlite3.OperationalError: no such column: foo

À partir de ce programme, je souhaite essentiellement un moyen plus simple de vérifier quelles données sont fournies et ce dont j'ai besoin pour interroger ma base de données avec

Modifier: J'ai maintenant essayé une méthode fournie:

def Create_Where_Condition(self, Details_Dic):
    print("In Where Condition")
    Where_Condition = ""
    for Key, Value in Details_Dic.items():
        print("Key:",Key)
        print("Value:", Value)
        if Value is not None:
            Prefix = " AND " if Where_Condition else " WHERE "
            Where_Condition += Prefix + "{}={}".format(Key, Value)
    return Where_Condition

def Get_Users_DB(self,Search_Data):
    print("In get_user_db")
    Details_Dic = Create_User_Dict((None, Search_Data[0], None, Search_Data[1], Search_Data[2]))
    print("after details_dic")
    SQL_Statement = "SELECT * FROM USERS" + self.Create_Where_Condition(Details_Dic)
    print("SQL STATEMENT:\n{}".format(SQL_Statement))
    try:  # Defensive programming to prevent database errors from stopping the program from running
        with global_lock:
            self.DB_Cursor.execute(SQL_Statement)
            User_Data = self.DB_Cursor.fetchall()
            print(User_Data)
        if User_Data:  # If any value was returned from the database
            User_Dict_List = []
            for User_Details in User_Data:  # For every user in the list convert them to a dictionary
                User_Dict = Create_User_Dict(User_Details)
                User_Dict_List.append(User_Dict)
            return User_Dict_List
        else:
            return False  # Tell called from function that the user does not exist

    except sqlite3.Error as Err:  # If an error occurs display a message in the console
        Error_Report(Err, "Get_User_DB")
        return False  # Tell called from function that the function was unsuccessful

Cependant maintenant j'obtiens l'erreur:

def Get_Users_DB(self, Search_Data):
    Details_Dic = Create_User_Dict((None,Search_Data[0],None,Search_Data[1],Search_Data[2]))
    try:  # Defensive programming to prevent database errors from stopping the program from running
        with global_lock:
            if Details_Dic["Username"]:
                # If a username is given, no other values need to be checked as username are unique
                self.DB_Cursor.execute("SELECT * FROM USERS WHERE username = ?", (Details_Dic["Username"],))
                # Selects user from USERS table based on the username provided
                User_Data = self.DB_Cursor.fetchall()
                # Fetches the user if applicable, returns as a list for processing purposes

            elif Details_Dic["Clearance"] and Details_Dic["Class_Count"] is not None:
                print("Here b0ss")
                # If there is a value for clearance and Class_Count is not a none type
                self.DB_Cursor.execute("SELECT * FROM USERS WHERE\
                                       clearance = ? AND Class_Count = ?",
                                       (Details_Dic["Clearance"], Details_Dic["Class_Count"]))
                # Select all users based on these restrictions
                User_Data = self.DB_Cursor.fetchall()

            elif Details_Dic["Clearance"]:  # If only a clearance level is given
                self.DB_Cursor.execute("SELECT * FROM USERS WHERE\
                                       clearance = ?", (Details_Dic["Clearance"],))
                User_Data = self.DB_Cursor.fetchall()
            elif Details_Dic["Class_Count"] is not None:  # If only a class value is given
                self.DB_Cursor.execute("SELECT * FROM USERS WHERE\
                                       Class_Count = ?", (Details_Dic["Class_Count"],))
                User_Data = self.DB_Cursor.fetchall()
            else:  # If no values are given, get all users
                self.DB_Cursor.execute("SELECT * FROM USERS")
                User_Data = self.DB_Cursor.fetchall()

        if User_Data:  # If any value was returned from the database
            User_Dict_List = []
            for User_Details in User_Data:  # For every user in the list convert them to a dictionary
                User_Dict = Create_User_Dict(User_Details)
                User_Dict_List.append(User_Dict)
            return User_Dict_List
        else:
            return False  # Tell called from function that the user does not exist

    except sqlite3.Error as Err:  # If an error occurs display a message in the console
        Error_Report(Err, "Get_User_DB")
        return False  # Tell called from function that the function was unsuccessful

où 'foo' est le nom d'utilisateur que je recherche


0 commentaires

3 Réponses :


0
votes

Et quelque chose comme ça? Faites de la requête une chaîne et une liste de paramètres construits avec les clauses if , vous pouvez alors coder une exécution / extraction. La pensée est quelque chose comme ceci dans le pseudo code:

query = "SELECT * from users WHERE 1 = 1"  
if username then query += "AND username = ?", params.push[username]  
else  
   if clearance then query += "AND clearance = ?", params.push[clearance]  
   if class_count then query += "AND class_count =?",params.push[class_count] 
execute(query,params) 

Le WHERE 1 = 1 est utilisé pour que la requête originale s'exécute et sélectionne toutes les lignes lorsqu'aucun paramètre n'est fourni .


0 commentaires

1
votes

Pour le moment, vos clés de dictionnaire ne correspondent pas à la casse des colonnes de votre table. Si vous pouviez changer cela, vous pourriez créer une fonction qui crée la condition WHERE pour vous:

def create_where_condition(details_dic, rows_to_include):
    where_condition = ""
    for key, value in details_dic.items():
        if key in rows_to_include and value is not None:
            if isinstance(value, str):
                value = '"' + value + '"'
            prefix = " AND " if where_condition else " WHERE "
            where_condition += prefix + '{}={}'.format(key, value)
    return where_condition

Cette approche a l'avantage qu'elle sera mise à l'échelle si vous le souhaitez inclure plus de lignes dans la clause WHERE sans avoir à ajouter des instructions supplémentaires if / elif .

Si votre details_dic contient également d'autres clés qui ne correspondent pas à colonnes de votre tableau ou que vous n'aimez pas inclure dans la clause WHERE, vous pouvez ajouter une liste blanche comme deuxième paramètre:

def create_where_condition(details_dic):
    where_condition = ""
    for key, value in details_dic.items():
        if value is not None:
            prefix = " AND " if where_condition else " WHERE "
            where_condition += prefix + '{}="{}"'.format(key, value)
    return where_condition

create_where_condition({"username": "Tom", "clearance": None, "Class_Count": 10})  # -> ' WHERE username=Tom AND Class_Count=10'
create_where_condition({"username": "Tom", "clearance": 100, "Class_Count": 10})  # -> ' WHERE username=Tom AND clearance=100 AND Class_Count=10'
create_where_condition({"username": None, "clearance": None, "Class_Count": None})  # -> ''


10 commentaires

J'ai essayé votre méthode, cependant chaque fois que j'exécute l'instruction (j'ajoute «SELECT * FROM TABLE»), cela me dit que «input_username» n'est pas une colonne.


C'est ce que je voulais dire avec le premier paragraphe. Si vous souhaitez utiliser cette fonction, vous devrez modifier vos clés de dictionnaire pour qu'elles correspondent au nom de vos colonnes. Je vois que vous avez ajouté ma fonction à votre message de démarrage et que vous obtenez une erreur pour une clé "foo", ce qui signifie que vous devez également inclure une liste blanche comme dans ma deuxième implémentation.


Je l'ai fait cependant. Pourquoi prend-il la partie 'foo' comme colonne de toute façon, si username est le nom de la colonne, pourquoi 'foo' serait-il utilisé?


Aka mon dictionnaire est {"Username": "Foo"}


J'ai découvert quelle est l'erreur. Dans le code que vous m'avez donné, si la valeur est une chaîne, elle n'est pas entourée de guillemets, ce qui signifie qu'une valeur de chaîne sera considérée comme une colonne


J'ai donc besoin de trouver un moyen pour que toutes les valeurs de chaîne soient entourées de guillemets supplémentaires


Vous m'avez battu d'une minute. J'ai mis à jour la réponse maintenant. Ma faute.


Cela ne fonctionne pas non plus, car les valeurs entières seraient alors comptées comme des chaînes. J'ai donc ajouté une autre instruction if 'If isinstance (Value, str): Value = "'" + Value + "'"


Vous avez raison, malheureusement j'ai rejeté votre proposition d'édition avant de lire votre commentaire. J'ai mis à jour la réponse.


Bon truc, je peux le sélectionner comme réponse maintenant. Aucune des autres solutions n'est mauvaise, mais c'est celle que j'ai utilisée



0
votes

Vous pouvez faire quelque chose comme ça à l'aide de chaînes f ( https: / /www.python.org/dev/peps/pep-0498/ ) et opérateurs ternaires:

    details_dict = create_user_dict((None, search_data[0], None, search_data[1], search_data[2]))
    with global_lock:
        sql_query = f"""
        SELECT * from USERS WHERE 1=1
        """
        # We can interpolate strings using dictionary keys
        for field_name in details_dict.keys():
            sql_query += f" AND {field_name}=%({field_name})s "
        self.db_cursor.execute(sql_query, details_dict)
    except sqlite3.Error as Err:
        Error_Report(Err, "Get_User_DB")
        return False```

Ou si les noms de vos colonnes DB correspondent aux noms de clés dans le details_dict, vous pouvez essayez cette approche:

def query_users(self, search_data):
    details_dict = create_user_dict((None, search_data[0], None, search_data[1], search_data[2]))
    with global_lock:
        # Named parameters in query looks like this %(<named_param>)s
        sql_query = f"""
        SELECT * from USERS WHERE 1=1
        {"AND username=%(username)s" if details_dict.get("username") else ""}
        {"AND clearance=%(clearance)s" if details_dict.get("clearance") else ""}
        {"AND Class_Count=%(class_count)s" if details_dict.get("class_count") else ""};
        """
        # Execute methods provides possibility of using named parameters which are already in the details_dict
        self.db_cursor.execute(sql_query, details_dict)
    except sqlite3.Error as Err:
        Error_Report(Err, "Get_User_DB")
        return False


0 commentaires