2016-08-24 2 views
0

J'essaie d'extraire des données via le package RODBC de Vertica DB. J'ai actuellement une requête SQL comme celle ci-dessous.Requête SQL paramétrée dans R avec la clause IN

library(rodbc) channel = odbcconnect("VerticaDB") query = paste 
(
     SELECT * 
     FROM item_history 
     WHERE item_exp_date BETWEEN ",x," AND ",y," 
     AND item_code IN ('A1', 
          'A2', 
          'B1', 
          'B2')",sep="")result = (sqlQuery(channel,query) 
) 

J'ai pu paramétrer les données passées dans la clause 'BETWEEN'. Existe-t-il un moyen de paramétrer les données transmises dans le fichier 'IN'?

Le nombre d'éléments de données transmis dans la clause 'IN' est également très élevé (plus de 100 éléments distincts).

Existe-t-il un moyen de passer d'un vecteur externe ou d'un fichier?

+2

OP et futurs lecteurs -VEUILLEZ être au courant que la tentative ou la réponse acceptée de OP n'est pas une véritable requête SQL paramétrée. @Benjamin est une meilleure tentative alors que d'autres concaténent simplement une chaîne SQL dynamique. – Parfait

Répondre

0

Pour ce faire, avec la manipulation de chaînes comme dans la question:

x <- "2000-01-01" 
y <- "2001-01-01" 
Item_Code <- c('A1','A2','B1','B2') 

query <- sprintf("select * from Item_History 
        where Item_Exp_Date between '%s' and '%s' 
         and Item_Code in (%s)", x, y, toString(shQuote(Item_Code, 'sh'))) 

On peut alternativement utiliser fn$ du package gsubfn pour l'interpolation de chaîne:

library(gsubfn) 
query2 <- fn$identity("select * from Item_History 
       where Item_Exp_Date between '$x' and '$y' 
       and Item_Code in (`toString(shQuote(Item_Code, 'sh'))`)") 
2

Ce que vous avez est l'injection SQL et non une requête paramétrée. Vous voudrez peut-être regarder le paquet RODBCext et son vignette.

Pour paramétrer correctement la requête, vous pouvez le faire

library(RODBC) 
library(RODBCext) 
channel = odbcConnect("VerticaDB") 
query = paste0("select * from Item_History ", 
       "where Item_Exp_Date between ? and ? ", 
       "and Item_Code = ?") 
item <- c("A1", "A2", "B1", "B2") 
x <- 3 
y <- 10 # I don't actually know what your x and y are, but hopefully you get the idea 
sqlExecute(
    channel = channel, 
    query = query, 
    data = list(x = rep(x, length(item)), 
       y = rep(y, length(item)), 
       item = item), 
    fetch = TRUE, 
    stringsAsFactors = FALSE 
) 

Cela a gros inconvénient, cependant, parce que sqlExecute va exécuter une requête pour chaque ligne de l'argument data (la liste sera sous la contrainte à une trame de données). Si vous avez des centaines d'éléments dans votre vecteur item, vous exécuterez des centaines de requêtes sur votre instance SQL, ce qui peut ne pas être particulièrement efficace.

Une méthode moins évidente consiste à écrire une procédure stockée pour construire la requête.

Votre procédure stockée dans SQL pourrait ressembler

CREATE PROCEDURE schema.specialQuery 
    @x int; 
    @y int; 
    @in varchar(2000); 
AS 
BEGIN 
    DECLARE @query = varchar(8000); 
    SET @query = 'select * from Item_History ' + 
       'where Item_Exp_Date between ' + convert(@x, varchar(10)) + 
         ' and ' + convert(@y, varchar(10)) + 
         ' and Item_Code IN (' + @in ')' 
    EXEC @query 
END 
GO 

Vous devrez peut-être jouer avec les convert fonctions et quelques-unes des citations, mais il fonctionnera avec

sqlExecute(
    channel = channel, 
    query = "EXECUTE schema.specialQuery @x = ?, @y = ?, @in = ?", 
    data = list(x = x, 
       y = y, 
       in = sprintf("'%s'", paste0(item, collapse = "', '"))), 
    fetch = TRUE, 
    stringsAsFactors = FALSE 
) 

Malheureusement, cette approche est toujours sensible aux problèmes avec les chaînes de caractères mal formatées passant par item, mais il est probablement plus rapide que d'exécuter des centaines de requêtes dans la première approche que j'ai affichée.