복붙노트

[SQL] 어떻게 Access에서 서로 다른 상황에서 VBA에서 매개 변수를 사용합니까?

SQL

어떻게 Access에서 서로 다른 상황에서 VBA에서 매개 변수를 사용합니까?

나는 SQL 주입에 대해 많이 읽고, bobby-tables.com 같은 소스에서, 매개 변수를 사용했습니다. 그러나, 나는 장소의 모든 종류의에서 문자열 연결과 동적 SQL을 많이 가지고 Access에서 복잡한 응용 프로그램과 함께 일하고 있어요.

그것은 다음과 같은 일 내가 변화를 원하고 오류를 방지하기 위해 매개 변수를 추가하고 나 잭 오코넬과 같은 작은 따옴표로 이름을 처리 할 수 ​​있습니다.

그것은 사용

쿼리는 대부분 다음과 같이 구성되어 있습니다 :

DoCmd.RunSQL "INSERT INTO Table1(Field1) SELECT Field1 FROM Table2 WHERE ID = " & Me.SomeTextbox

쿼리의 서로 다른 종류의 매개 변수를 사용하여 내 옵션은 무엇입니까?

이 질문은 내가 여러 게시물에 매개 변수를 주석을 사용하여 어떻게 자주를 들어, 자원위한 것입니다

해결법

  1. ==============================

    1.조회에 매개 변수를 사용하는 방법에는 여러 가지가 있습니다. 나는 그들의 대부분에 대한 예제를 제공하기 위해 노력할 것입니다, 그들은 해당하는 곳이다.

    조회에 매개 변수를 사용하는 방법에는 여러 가지가 있습니다. 나는 그들의 대부분에 대한 예제를 제공하기 위해 노력할 것입니다, 그들은 해당하는 곳이다.

    첫째, 우리는 폼, 보고서 및 도메인 집계 등의 접근에 고유 한 솔루션을 논의 할 것이다. 그런 다음, 우리는 DAO 및 ADO에 대해 이야기 할 것이다.

    Access에서, 당신은 직접 SQL 코드에서 폼과 보고서의 컨트롤의 현재의 값을 사용할 수 있습니다. 이 매개 변수에 대한 필요성을 제한합니다.

    다음과 같은 방법으로 컨트롤을 참조 할 수 있습니다 :

    양식! MyForm을!합니다 MyTextBox 폼의 간단한 제어를위한

    양식! 하위 폼의 컨트롤에 대한 MyForm을! MySubform.Form!합니다 MyTextBox

    보고서의 컨트롤에 대한 보고서! MyReport를!합니다 MyTextBox

    샘플 구현 :

    DoCmd.RunSQL "INSERT INTO Table1(Field1) SELECT Forms!MyForm!MyTextbox" 'Inserts a single value
    DoCmd.RunSQL "INSERT INTO Table1(Field1) SELECT Field1 FROM Table2 WHERE ID = Forms!MyForm!MyTextbox" 'Inserts from a different table
    

    이것은 다음과 같은 용도로 사용할 수 있습니다 :

    DoCmd.RunSQL, 일반 (GUI의) 쿼리, 폼 및 보고서의 레코드 원본, 양식 및 보고서 필터, 도메인 집계 DoCmd.OpenForm 및 DoCmd.OpenReport를 사용하는 경우

    이것은 다음과 같은 용도로 사용할 수 없습니다 :

    DAO 또는 ADODB하여 쿼리 실행할 때 (예를 들면 개구 레코드를 CurrentDb.Execute)

    Access에서 TempVars 전 세계적으로 VBA에서 설정하거나 매크로의를 사용할 수있는 사용 가능한 변수입니다. 그들은 여러 쿼리를 다시 사용할 수 있습니다.

    샘플 구현 :

    TempVars!MyTempVar = Me.MyTextbox.Value 'Note: .Value is required
    DoCmd.RunSQL "INSERT INTO Table1(Field1) SELECT Field1 FROM Table2 WHERE ID = TempVars!MyTempVar"
    TempVars.Remove "MyTempVar" 'Unset TempVar when you're done using it
    

    ADO와 DAO를 사용할 수 없습니다, 다른 용도로 사용할 수 : TempVars에 대한 가용성 폼과 보고서의 값의 그것과 동일하다.

    나는 개체의 개방이 종료되면, TempVars 사용할 수있어 이후, 제어 이름을 참조를 통해 폼이나 보고서를 열 때 매개 변수를 사용 TempVars하는 것이 좋습니다. 나는 양식이나 보고서를 새로 고칠 때 불확실성을 피하기 위해, 모든 폼 또는 보고서에 대해 고유 한 TempVar 이름을 사용하는 것이 좋습니다.

    많은 TempVars처럼, 당신은 저장소에 사용자 정의 기능과 정적 변수를 사용하여 값을 검색 할 수 있습니다.

    샘플 구현 :

    Option Compare Database
    Option Explicit
    
    Private ThisDate As Date
    
    
    Public Function GetThisDate() As Date
        If ThisDate = #12:00:00 AM# Then
            ' Set default value.
            ThisDate = Date
        End If 
        GetThisDate = ThisDate
    End Function
    
    
    Public Function SetThisDate(ByVal NewDate As Date) As Date
        ThisDate = NewDate
        SetThisDate = ThisDate
    End Function
    

    그리고:

    SetThisDate SomeDateValue ' Will store SomeDateValue in ThisDate.
    DoCmd.RunSQL "INSERT INTO Table1(Field1) SELECT Field1 FROM Table2 WHERE [SomeDateField] = GetThisDate()"
    

    또한, 선택적인 매개 변수를 갖는 단일 기능의 설정과 전용 정적 변수의 값을 얻는 모두 생성 될 수있다 :

    Public Function ThisValue(Optional ByVal Value As Variant) As Variant
        Static CurrentValue As Variant
        ' Define default return value.
        Const DefaultValue  As Variant = Null
    
        If Not IsMissing(Value) Then
            ' Set value.
            CurrentValue = Value
        ElseIf IsEmpty(CurrentValue) Then
            ' Set default value
            CurrentValue = DefaultValue
        End If
        ' Return value.
        ThisValue = CurrentValue
    End Function
    

    값을 설정하려면 :

    ThisValue "Some text value"
    

    값을 얻으려면 :

    CurrentValue = ThisValue
    

    쿼리에서 :

    ThisValue "SomeText"  ' Set value to filter on.
    DoCmd.RunSQL "INSERT INTO Table1(Field1) SELECT Field1 FROM Table2 WHERE [SomeField] = ThisValue()"
    

    나는 간단한 수 있습니다 있도록 DoCmd.SetParameter의 사용은 오히려 제한됩니다. 그것은 당신이 DoCmd.OpenForm, DoCmd.OpenReport 및 다른 DoCmd 문에서 사용할 수있는 매개 변수를 설정할 수 있습니다,하지만 DoCmd.RunSQL, 필터, DAO 및 ADO로 작업을하지 않습니다.

    샘플 구현

    DoCmd.SetParameter "MyParameter", Me.MyTextbox
    DoCmd.OpenForm "MyForm",,, "ID = MyParameter"
    

    DAO에서, 우리는 설정 매개 변수를 쿼리를 만들 수 DAO.QueryDef 객체를 사용하여 다음 중 레코드를 열거 나 쿼리를 실행할 수 있습니다. 먼저 다음 매개 변수를 설정 QueryDef.Parameters 모음을 사용하여 쿼리 'SQL을 설정합니다.

    내 예제에서는 암시 적 매개 변수 유형을 사용하는거야. 당신이 그 (것)들을 명시 적으로 확인하려면 쿼리에 매개 변수 선언을 추가합니다.

    샘플 구현

    'Execute query, unnamed parameters
    With CurrentDb.CreateQueryDef("", "INSERT INTO Table1(Field1) SELECT Field1 FROM Table2 WHERE Field1 = ?p1 And Field2 = ?p2")
        .Parameters(0) = Me.Field1
        .Parameters(1) = Me.Field2
        .Execute
    End With
    
    'Open recordset, named parameters
    Dim rs As DAO.Recordset
    With CurrentDb.CreateQueryDef("", "SELECT Field1 FROM Table2 WHERE Field1 = FirstParameter And Field2 = SecondParameter")
        .Parameters!FirstParameter = Me.Field1 'Bang notation
        .Parameters("SecondParameter").Value = Me.Field2 'More explicit notation
        Set rs = .OpenRecordset
    End With
    

    이 DAO에서만 사용할 수 있지만, 당신은 그들이 같은 형태의 레코드, 목록 상자 레코드와 콤보 상자 레코드와 같은 매개 변수를 사용하기 위해 DAO 레코드 집합에 많은 일을 설정할 수 있습니다. 액세스 텍스트, 그리고 레코드를 사용하기 때문에 정렬 및 필터링 할 때 당신이 할 경우, 그 상황이 문제가 될 수 있습니다.

    당신은의 ADODB.Command 개체를 사용하여 ADO에서 매개 변수를 사용할 수 있습니다. 사용 Command.CreateParameter 매개 변수를 만든 다음 Command.Parameters의 컬렉션에 추가합니다.

    명시 적으로 매개 변수를 선언하는 ADO에서 또한 .Parameters 모음을 사용하거나 암시 적으로 매개 변수를 전달에 Command.Execute 방법에 매개 변수 배열을 전달할 수 있습니다.

    ADO는 명명 된 매개 변수를 지원하지 않습니다. 당신이 이름을 통과 할 수 있지만,이 처리되지 것.

    샘플 구현 :

    'Execute query, unnamed parameters
    Dim cmd As ADODB.Command
    Set cmd = New ADODB.Command
    With cmd
        Set .ActiveConnection = CurrentProject.Connection 'Use a connection to the current database
        .CommandText = "INSERT INTO Table1(Field1) SELECT Field1 FROM Table2 WHERE Field1 = ? And Field2 = ?"
        .Parameters.Append .CreateParameter(, adVarWChar, adParamInput, Len(Me.Field1), Me.Field1) 'adVarWChar for text boxes that may contain unicode
        .Parameters.Append .CreateParameter(, adInteger, adParamInput, 8, Me.Field2) 'adInteger for whole numbers (long or integer)
        .Execute
    End With
    
    'Open recordset, implicit parameters
    Dim rs As ADODB.Recordset
    Dim cmd As ADODB.Command
    Set cmd = New ADODB.Command
    With cmd
        Set .ActiveConnection = CurrentProject.Connection 'Use a connection to the current database
        .CommandText = "SELECT Field1 FROM Table2 WHERE Field1 = @FirstParameter And Field2 = @SecondParameter"
         Set rs = .Execute(,Array(Me.Field1, Me.Field2))
    End With
    

    개구 DAO의 레코드와 동일한 제한이 적용된다. 이 방법은 쿼리 및 열기 레코드 집합을 실행에 국한되는 동안, 당신은 당신의 응용 프로그램의 다른 곳에서 그 레코드를 사용할 수 있습니다.

  2. ==============================

    2.나는 문자열 연결의 혼란을 해결하기 위해 명명 된 매개 변수의 부족을 처리하기 위해 상당히 기본적인 쿼리 빌더 클래스를 구축했다. 쿼리를 생성하는 것은 매우 간단하다.

    나는 문자열 연결의 혼란을 해결하기 위해 명명 된 매개 변수의 부족을 처리하기 위해 상당히 기본적인 쿼리 빌더 클래스를 구축했다. 쿼리를 생성하는 것은 매우 간단하다.

    Public Function GetQuery() As String
    
        With New MSAccessQueryBuilder
            .QueryBody = "SELECT * FROM tblEmployees"
    
            .AddPredicate "StartDate > @StartDate OR StatusChangeDate > @StartDate"
            .AddPredicate "StatusIndicator IN (@Active, @LeaveOfAbsence) OR Grade > @Grade"
            .AddPredicate "Salary > @SalaryThreshhold"
            .AddPredicate "Retired = @IsRetired"
    
            .AddStringParameter "Active", "A"
            .AddLongParameter "Grade", 10
            .AddBooleanParameter "IsRetired", False
            .AddStringParameter "LeaveOfAbsence", "L"
            .AddCurrencyParameter "SalaryThreshhold", 9999.99@
            .AddDateParameter "StartDate", #3/29/2018#
    
            .QueryFooter = "ORDER BY ID ASC"
            GetQuery = .ToString
    
        End With
    
    End Function
    

    추천 toString () 메서드 외모의 출력 :

    각 술어는 연결 및 / 또는 절을 처리하기 위해 괄호에 싸여와 같은 이름을 가진 매개 변수는 한 번만 선언 할 필요가있다. 전체 코드는 내 GitHub의에 있으며 아래의 재현. 또한 ADODB 매개 변수를 사용하여 오라클 통과 쿼리에 대한 버전이있다. 결국, 나는 IQueryBuilder 인터페이스 모두를 포장하고 싶습니다.

    VERSION 1.0 CLASS
    BEGIN
      MultiUse = -1  'True
    END
    Attribute VB_Name = "MSAccessQueryBuilder"
    Attribute VB_GlobalNameSpace = False
    Attribute VB_Creatable = True
    Attribute VB_PredeclaredId = False
    Attribute VB_Exposed = True
    '@Folder("VBALibrary.Data")
    '@Description("Provides tools to construct Microsoft Access SQL statements containing predicates and parameters.")
    
    Option Explicit
    
    Private Const mlngErrorNumber As Long = vbObjectError + 513
    Private Const mstrClassName As String = "MSAccessQueryBuilder"
    Private Const mstrParameterExistsErrorMessage As String = "A parameter with this name has already been added to the Parameters dictionary."
    
    Private Type TSqlBuilder
        QueryBody As String
        QueryFooter As String
    End Type
    
    Private mobjParameters As Object
    Private mobjPredicates As Collection
    Private this As TSqlBuilder
    
    
    ' =============================================================================
    ' CONSTRUCTOR / DESTRUCTOR
    ' =============================================================================
    
    Private Sub Class_Initialize()
        Set mobjParameters = CreateObject("Scripting.Dictionary")
        Set mobjPredicates = New Collection
    End Sub
    
    
    ' =============================================================================
    ' PROPERTIES
    ' =============================================================================
    
    '@Description("Gets or sets the query statement (SELECT, INSERT, UPDATE, DELETE), exclusive of any predicates.")
    Public Property Get QueryBody() As String
        QueryBody = this.QueryBody
    End Property
    Public Property Let QueryBody(ByVal Value As String)
        this.QueryBody = Value
    End Property
    
    '@Description("Gets or sets post-predicate query statements (e.g., GROUP BY, ORDER BY).")
    Public Property Get QueryFooter() As String
        QueryFooter = this.QueryFooter
    End Property
    Public Property Let QueryFooter(ByVal Value As String)
        this.QueryFooter = Value
    End Property
    
    
    ' =============================================================================
    ' PUBLIC METHODS
    ' =============================================================================
    
    '@Description("Maps a boolean parameter and its value to the query builder.")
    '@Param("strName: The parameter's name.")
    '@Param("blnValue: The parameter's value.")
    Public Sub AddBooleanParameter(ByVal strName As String, ByVal blnValue As Boolean)
        If mobjParameters.Exists(strName) Then
            Err.Raise mlngErrorNumber, mstrClassName & ".AddBooleanParameter", mstrParameterExistsErrorMessage
        Else
            mobjParameters.Add strName, CStr(blnValue)
        End If
    End Sub
    
    ' =============================================================================
    
    '@Description("Maps a currency parameter and its value to the query builder.")
    '@Param("strName: The parameter's name.")
    '@Param("curValue: The parameter's value.")
    Public Sub AddCurrencyParameter(ByVal strName As String, ByVal curValue As Currency)
        If mobjParameters.Exists(strName) Then
            Err.Raise mlngErrorNumber, mstrClassName & ".AddCurrencyParameter", mstrParameterExistsErrorMessage
        Else
            mobjParameters.Add strName, CStr(curValue)
        End If
    End Sub
    
    ' =============================================================================
    
    '@Description("Maps a date parameter and its value to the query builder.")
    '@Param("strName: The parameter's name.")
    '@Param("dtmValue: The parameter's value.")
    Public Sub AddDateParameter(ByVal strName As String, ByVal dtmValue As Date)
        If mobjParameters.Exists(strName) Then
            Err.Raise mlngErrorNumber, mstrClassName & ".AddDateParameter", mstrParameterExistsErrorMessage
        Else
            mobjParameters.Add strName, "#" & CStr(dtmValue) & "#"
        End If
    End Sub
    
    ' =============================================================================
    
    '@Description("Maps a long parameter and its value to the query builder.")
    '@Param("strName: The parameter's name.")
    '@Param("lngValue: The parameter's value.")
    Public Sub AddLongParameter(ByVal strName As String, ByVal lngValue As Long)
        If mobjParameters.Exists(strName) Then
            Err.Raise mlngErrorNumber, mstrClassName & ".AddNumericParameter", mstrParameterExistsErrorMessage
        Else
            mobjParameters.Add strName, CStr(lngValue)
        End If
    End Sub
    
    ' =============================================================================
    
    '@Description("Adds a predicate to the query's WHERE criteria.")
    '@Param("strPredicate: The predicate text to be added.")
    Public Sub AddPredicate(ByVal strPredicate As String)
        mobjPredicates.Add "(" & strPredicate & ")"
    End Sub
    
    ' =============================================================================
    
    '@Description("Maps a string parameter and its value to the query builder.")
    '@Param("strName: The parameter's name.")
    '@Param("strValue: The parameter's value.")
    Public Sub AddStringParameter(ByVal strName As String, ByVal strValue As String)
        If mobjParameters.Exists(strName) Then
            Err.Raise mlngErrorNumber, mstrClassName & ".AddStringParameter", mstrParameterExistsErrorMessage
        Else
            mobjParameters.Add strName, "'" & strValue & "'"
        End If
    End Sub
    
    ' =============================================================================
    
    '@Description("Parses the query, its predicates, and any parameter values, and outputs an SQL statement.")
    '@Returns("A string containing the parsed query.")
    Public Function ToString() As String
    
    Dim strPredicatesWithValues As String
    
        Const strErrorSource As String = "QueryBuilder.ToString"
    
        If this.QueryBody = vbNullString Then
            Err.Raise mlngErrorNumber, strErrorSource, "No query body is currently defined. Unable to build valid SQL."
        End If
        ToString = this.QueryBody
    
        strPredicatesWithValues = ReplaceParametersWithValues(GetPredicatesText)
        EnsureParametersHaveValues strPredicatesWithValues
    
        If Not strPredicatesWithValues = vbNullString Then
            ToString = ToString & " " & strPredicatesWithValues
        End If
    
        If Not this.QueryFooter = vbNullString Then
            ToString = ToString & " " & this.QueryFooter & ";"
        End If
    
    End Function
    
    
    ' =============================================================================
    ' PRIVATE METHODS
    ' =============================================================================
    
    '@Description("Ensures that all parameters defined in the query have been provided a value.")
    '@Param("strQueryText: The query text to verify.")
    Private Sub EnsureParametersHaveValues(ByVal strQueryText As String)
    
    Dim strUnmatchedParameter As String
    Dim lngMatchedPoisition As Long
    Dim lngWordEndPosition As Long
    
        Const strProcedureName As String = "EnsureParametersHaveValues"
    
        lngMatchedPoisition = InStr(1, strQueryText, "@", vbTextCompare)
        If lngMatchedPoisition <> 0 Then
            lngWordEndPosition = InStr(lngMatchedPoisition, strQueryText, Space$(1), vbTextCompare)
            strUnmatchedParameter = Mid$(strQueryText, lngMatchedPoisition, lngWordEndPosition - lngMatchedPoisition)
        End If
    
        If Not strUnmatchedParameter = vbNullString Then
            Err.Raise mlngErrorNumber, mstrClassName & "." & strProcedureName, "Parameter " & strUnmatchedParameter & " has not been provided a value."
        End If
    
    End Sub
    
    ' =============================================================================
    
    '@Description("Combines each predicate in the predicates collection into a single string statement.")
    '@Returns("A string containing the text of all predicates added to the query builder.")
    Private Function GetPredicatesText() As String
    
    Dim strPredicates As String
    Dim vntPredicate As Variant
    
        If mobjPredicates.Count > 0 Then
            strPredicates = "WHERE 1 = 1"
            For Each vntPredicate In mobjPredicates
                strPredicates = strPredicates & " AND " & CStr(vntPredicate)
            Next vntPredicate
        End If
    
        GetPredicatesText = strPredicates
    
    End Function
    
    ' =============================================================================
    
    '@Description("Replaces parameters in the predicates statements with their provided values.")
    '@Param("strPredicates: The text of the query's predicates.")
    '@Returns("A string containing the predicates text with its parameters replaces by their provided values.")
    Private Function ReplaceParametersWithValues(ByVal strPredicates As String) As String
    
    Dim vntKey As Variant
    Dim strParameterName As String
    Dim strParameterValue As String
    Dim strPredicatesWithValues As String
    
        Const strProcedureName As String = "ReplaceParametersWithValues"
    
        strPredicatesWithValues = strPredicates
        For Each vntKey In mobjParameters.Keys
            strParameterName = CStr(vntKey)
            strParameterValue = CStr(mobjParameters(vntKey))
    
            If InStr(1, strPredicatesWithValues, "@" & strParameterName, vbTextCompare) = 0 Then
                Err.Raise mlngErrorNumber, mstrClassName & "." & strProcedureName, "Parameter " & strParameterName & " was not found in the query."
            Else
                strPredicatesWithValues = Replace(strPredicatesWithValues, "@" & strParameterName, strParameterValue, 1, -1, vbTextCompare)
            End If
        Next vntKey
    
        ReplaceParametersWithValues = strPredicatesWithValues
    
    End Function
    
    ' =============================================================================
    
  3. from https://stackoverflow.com/questions/49509615/how-do-i-use-parameters-in-vba-in-the-different-contexts-in-microsoft-access by cc-by-sa and MIT license