Comparar tablas Excel (versión mejorada)

Versión mejorada de la herramienta para comparar tablas Excel mediante Access, que se describe detalladamente en

Comparar tablas excel (con access) – Abraza la Web

En esta bnueva versión se ha mejorado algo el código y añadido funcionalidades nuevas.

  • La librería/biblioteca de Excel para VBA se carga ahora de modo «late binding» para evitar errores en las referencias.
  • Se añade un contador de progreso para ver por dónde van los cálculos
  • Se añade un nuevo campo en la tabla excel de resultados con los valores que se han quitado (OUT) y añadido (IN) al comparar la tabla derecha con la izquierda, aunque no se muestran valores duplicados, sirve para ver fácilmente los cambios (se puede seleccionar esta funcionalidad con un checkbox). Cuando dos campos son iguales en las tablas, este nuevo campo IN/OUT se muestra vacío evidentemen

La tabla Excel resultado es ahora así, mostrando claramente qué es lo que cambia

El enlace para descarga:

El código fuente de la aplicación (formulario). Se incluyen comentarios y parte de las distintas pruebas realizadas para optimización (comentadas)

Option Compare Database
Const camposCombo1 = "campo1_1,campo1_2,campo1_3,campo1_4,campo1_5,campo1_6,campo1_7" ' para usar en arrays en el código y permitir escalabilidad
Const camposCombo2 = "campo2_1,campo2_2,campo2_3,campo2_4,campo2_5,campo2_6,campo2_7" ' para usar en arrays en el código



Private Sub actualizarTablas_Click()
Call Form_Load
End Sub



Private Sub comandoEjecutar_Click()

' late binding the Excel object  por errores en algunos PCs

Dim xlApp As Object 'Excel.Application
Dim xlWB As Object 'Excel.Workbook
Dim xlSh As Object 'Excel.Worksheet
Dim row As Integer
Dim col As Integer
Dim count As Integer

Dim db As DAO.Database
Dim rst1 As DAO.Recordset
Dim rst2 As DAO.Recordset

Dim seleccion As String
Dim total As String
Dim campo1 As String
Dim campo2 As String

Dim campos1(20) As String
Dim campos2(20) As String

Dim valor As Variant
Dim i As Integer
Dim j As Integer

Dim strQuery As String

On Error GoTo ERROR


'inicializar
DoCmd.Hourglass True

' comprobar que el campo a comprobar está seleccionado en ambas tablas
If comboCampo1 = "" Or comboCampo2 = "" Then
        MsgBox ("Alguno de los campos comunes no se ha seleccionado")
        Exit Sub
End If

' limpiar las tablas para que no haya nulos
quitarNulos (comboTablas1)
quitarNulos (comboTablas2)

For i = 0 To UBound(campos1) - LBound(campos1) - 1
    campos1(i) = ""
    campos2(i) = ""
Next
count = 1
row = 1
col = 1

Set db = CurrentDb()

'------------------------------------------
' Búsqueda de duplicados en ambas tablas
'------------------------------------------

' Para las primeras consultas
campo1 = "[" & comboTablas1 & "].[" & comboCampo1 & "]"
campo2 = "[" & comboTablas2 & "].[" & comboCampo2 & "]"

strQuery = "SELECT " & campo1 & ",* FROM [" & comboTablas1 & "] " _
    & "WHERE (((" & campo1 & ") In (SELECT [" & comboCampo1 & "] " _
    & "FROM [" & comboTablas1 & "] As Tmp GROUP BY [" & comboCampo1 & "] HAVING Count(*)>1 ))) " _
    & "ORDER BY " & campo1 & ";"
'abrir consulta
Set rst1 = db.OpenRecordset(strQuery)

strQuery = "SELECT " & campo2 & ",* FROM [" & comboTablas2 & "] " _
    & "WHERE (((" & campo2 & ") In (SELECT [" & comboCampo2 & "] " _
    & "FROM [" & comboTablas2 & "] As Tmp GROUP BY [" & comboCampo2 & "] HAVING Count(*)>1 ))) " _
    & "ORDER BY " & campo2 & ";"
'abrir consulta
Set rst2 = db.OpenRecordset(strQuery)


' abrir excel de resultados
'Set xlApp = New Excel.Application
Set xlApp = CreateObject("Excel.Application") ' late binding
Set xlWB = xlApp.Workbooks.Add ' crea un nuevo libro
Set xlSh = xlWB.Sheets(1) '

'xlApp.Visible = True
xlApp.Visible = False
textLog.SetFocus
Beep
textLog.SetFocus
textLog.Text = "Buscando duplicados..."
DoEvents

' Duplicados tabla 1

xlWB.Sheets(1).Name = Left("Duplicados " & comboTablas1, 30) ' error en tabs con monbres más largos

Call rellenarExcelRst(xlSh, rst1)
'Call rellenarExcel(xlSh, rst1, row, comboCampo1, comboTablas1)

' Duplicados tabla 2
xlWB.Sheets.Add After:=xlWB.Sheets(1)
Set xlSh = xlWB.Sheets(2) '
xlWB.Sheets(2).Name = Left("Duplicados " & comboTablas2, 30)

'Call rellenarExcel(xlSh, rst2, row, comboCampo2, comboTablas2)
Call rellenarExcelRst(xlSh, rst2)

rst1.Close
rst2.Close

'------------------------------------------
' NO COINCIDENTES
'------------------------------------------
textLog.SetFocus
textLog.Text = "Buscando No Coincidentes..."
DoEvents

strQuery = "SELECT * FROM [" & comboTablas1 & "] LEFT JOIN [" & comboTablas2 & "] ON " _
        & campo1 & " = " & campo2 & " WHERE (((" & campo2 & ") Is Null));"


'abrir consulta
Set rst1 = db.OpenRecordset(strQuery)

strQuery = "SELECT * FROM [" & comboTablas2 & "] LEFT JOIN [" & comboTablas1 & "] ON " _
        & campo2 & " = " & campo1 & " WHERE (((" & campo1 & ") Is Null));"

' abrir consulta
Set rst2 = db.OpenRecordset(strQuery)

' Tabla1 no en Tabla2
xlWB.Sheets.Add After:=xlWB.Sheets(2)

Set xlSh = xlWB.Sheets(3) '
xlWB.Sheets(3).Name = Left(comboTablas1 & " FALTAN EN " & comboTablas2, 30)

'row = 1
'Call rellenarExcel(xlSh, rst1, row, comboCampo1, comboTablas1)
Call rellenarExcelRst(xlSh, rst1)

' inmovilizar fila superior
With xlApp.ActiveWindow
            .SplitColumn = 0
            .SplitRow = 1
            .FreezePanes = True
End With
    
' Tabla 2 no en Tabla1
xlWB.Sheets.Add After:=xlWB.Sheets(3)

Set xlSh = xlWB.Sheets(4) '
xlWB.Sheets(4).Name = Left(comboTablas2 & " FALTAN EN " & comboTablas1, 30)

'row = 1
'Call rellenarExcel(xlSh, rst2, row, comboCampo2, comboTablas2)
Call rellenarExcelRst(xlSh, rst2)

' inmovilizar fila superior
With xlApp.ActiveWindow
            .SplitColumn = 0
            .SplitRow = 1
            .FreezePanes = True
End With
rst1.Close
rst2.Close

'------------------------------------------
' Diffs Tabla 1 vs Tabla2
'------------------------------------------
textLog.SetFocus
textLog.Text = "Buscando Diferencias en los campos..."
DoEvents
' rellenar array campos1
i = 0
For Each valor In Split(camposCombo1, ",")
        If Not IsNull(Me.Controls(valor)) Then
            campos1(i) = Me.Controls(valor)
        Else
            campos1(i) = ""
        End If
        i = i + 1
Next
' rellenar array campos2
i = 0
For Each valor In Split(camposCombo2, ",")
        If Not IsNull(Me.Controls(valor)) Then
            campos2(i) = Me.Controls(valor)
        Else
            campos2(i) = ""
        End If
        i = i + 1
Next

' chequeos de campos y creación consulta
seleccion = ""
total = ""

i = 0

Do While campos1(i) <> ""
    If campos2(i) = "" Then
        MsgBox ("Campo Tabla 2 vacío")
        Exit Sub
    Else
        campo1 = "[" & comboTablas1 & "].[" & campos1(i) & "]"
        campo2 = "[" & comboTablas2 & "].[" & campos2(i) & "]"
        If checkBoxLimpiar.Value = True Then
            seleccion = seleccion & "," & campo1 & "," & campo2 & ", Similitud_adpt(limpiar(" & campo1 & "),limpiar(" & campo2 & ")) AS Expr" & i + 1
        Else
            seleccion = seleccion & "," & campo1 & "," & campo2 & ", Similitud_adpt(" & campo1 & "," & campo2 & ") AS Expr" & i + 1
        End If
        
        If checkBoxInOut.Value = True Then
            seleccion = seleccion & " , inoutDiff(" & campo1 & "," & campo2 & ") AS ExprInOut" & i + 1
        End If
        
        
        If i = 0 Then
            total = ", [Expr" & i + 1 & "]"
        Else
            total = total & "+[Expr" & i + 1 & "]"
        End If
    End If
    
    i = i + 1
Loop

' añadimos la expresión con el valor total de las similitudes
If total <> "" Then total = total & " AS ExprTotal"

' resto de campòs de las tablas
If CheckboxTabla1.Value = True Then
    total = total & ", [" & comboTablas1 & "].*"
End If

If CheckboxTabla2.Value = True Then
    total = total & ", [" & comboTablas2 & "].*"
End If

campo1 = "[" & comboTablas1 & "].[" & comboCampo1 & "]"
campo2 = "[" & comboTablas2 & "].[" & comboCampo2 & "]"

' seleccion y total ya llevan la primera ","
strQuery = "SELECT " & campo1 & seleccion & total & " FROM [" & comboTablas1 & "] INNER JOIN " _
            & "[" & comboTablas2 & "] ON " & campo1 & " = " & campo2 & ";"
' No limpio los campos de la inner join, pues tarda demasiado, hacer esto previamente
'If checkBoxLimpiar.Value = True Then
'    strquery = "SELECT " & campo1 & seleccion & total & " FROM [" & comboTablas1 & "] INNER JOIN " _
'            & "[" & comboTablas2 & "] ON limpiar(" & campo1 & ") = limpiar(" & campo2 & ");"
'End If
' abrir consulta
Set rst1 = db.OpenRecordset(strQuery)


' tratar de vcrear consulta de creación de tabla
' falla con error en "campos solo se pueden crear unan vez es por el incluir el * al final"
' cuando seleccionamos el resto de campos, pero también tarda lo mismo que de las otras maneras
'Dim strOut As String
'strOut = Replace(strQuery, "FROM", " INTO [Diffs] FROM")
'CurrentDb.Execute strOut

' Diffs Tabla1 vs Tabla2
xlWB.Sheets.Add After:=xlWB.Sheets(4)

Set xlSh = xlWB.Sheets(5) '
xlWB.Sheets(5).Name = Left("DIFFS " & comboTablas1 & " vs " & comboTablas2, 30)

row = 1
xlApp.Calculation = xlManual

' Probamos otra manera
' el tiempo se va en crear la tabla (copiarla desde la consulta) pues tarda muchísimo en ello
' VERIFICADO: el tiempo es idéntico de cualquier manera
' copyfromrecorset = crear tabla opción 1 (Insert INTO) = crear tabla opción 2 (bucles anidados y copiar rst)

Call guardarConsulta(strQuery, rst1) ' creamos una tabla y consulta temporales adicionales

' Volcamos a Excel el nuevo rst de la tabla creada, que ya no tiene que
' calcular las fórmulas y es inmediato

Set rst1 = db.OpenRecordset("SELECT * FROM Diffs;")
'Call rellenarExcel(xlSh, rst1, row, comboCampo1, comboTablas1) ' rellena excel registro a registro
Call rellenarExcelRst(xlSh, rst1) ' rellena excel con copyFromRecordset


xlApp.Calculation = xlAutomatic
' inmovilizar fila superior
With xlApp.ActiveWindow
            .SplitColumn = 0
            .SplitRow = 1
            .FreezePanes = True
End With
Beep
textLog.Text = "Coloreando tabla Excel..."
DoEvents

Call pintarExcel(xlSh, rst1)

rst1.Close
db.Close
Set rst1 = Nothing
Set rst2 = Nothing
Set db = Nothing

Beep
textLog.SetFocus
textLog.Text = ""
DoEvents
DoCmd.Hourglass False

' eliminamos la tabla temporal
DoCmd.SetWarnings (WarningsOff)
If IsNull(DLookup("name", "msysobjects", "name='Diffs'")) Then
    ' nada
Else
    CurrentDb.TableDefs.Delete "Diffs"
End If
DoCmd.SetWarnings True

MsgBox ("FIN")

xlApp.Visible = True
Exit Sub
ERROR:
    MsgBox ("Ha ocurrido un error" & vbCrLf & vbCrLf & Err.Description & vbCrLf & vbCrLf & "Se recargará el formulario")
    DoCmd.Hourglass False
    xlWB.Close
    Call Form_Load
    Exit Sub
End Sub
Private Sub guardarConsulta(strQuery As String, rst As DAO.Recordset)
' https://stackoverflow.com/questions/12608646/in-vba-how-does-one-make-a-table-from-a-recordset
On Error GoTo ERROR

DoCmd.SetWarnings (WarningsOff)

' para la opción 2 no necesito la consulta
'If IsNull(DLookup("name", "msysobjects", "name='Comparación'")) Then
'    CurrentDb.CreateQueryDef "Comparación", strQuery
'Else
'    CurrentDb.QueryDefs("Comparación").SQL = strQuery
'End If

' importar en access como tabla
If IsNull(DLookup("name", "msysobjects", "name='Diffs'")) Then
    ' nada
Else
    CurrentDb.TableDefs.Delete "Diffs"
End If



' crear tabla Diffs
'-------------------------------------------------------------------
Dim tmpTabla As TableDef
Dim i As Integer
Dim fld As Field
Dim newFld As Field
Dim idx As Index

Set tmpTabla = CurrentDb.CreateTableDef("Diffs")

tmpTabla.Name = "Diffs"

' crear campos, primero el Id

Set fld = tmpTabla.CreateField("IDN", dbLong)
fld.Attributes = fld.Attributes + dbAutoIncrField
fld.OrdinalPosition = 1
tmpTabla.Fields.Append fld

' ahora copiamos los de la consulta, reemplazar el "." pq si no da error
For Each fld In rst.Fields
    Set newFld = tmpTabla.CreateField(Replace(fld.Name, ".", "_"), fld.Type, fld.Size)
    If fld.Type = dbText Then newFld.AllowZeroLength = True     ' para permitir valores vacíos y nulos y no tener errores
    tmpTabla.Fields.Append newFld
    'tmpTabla.Fields.Append tmpTabla.CreateField(Replace(fld.Name, ".", "_"), fld.Type, fld.Size)
Next

'clave primaria
tmpTabla.Fields.Refresh
Set idx = tmpTabla.CreateIndex("PrimaryKey")
Set fld = idx.CreateField("IDN", dbLong)
idx.Fields.Append fld
idx.Primary = True
tmpTabla.Indexes.Append idx

CurrentDb.TableDefs.Append tmpTabla
CurrentDb.TableDefs.Refresh

' llenar tabla con los valores de la consulta
'strQuery = "SELECT * INTO Diffs FROM Comparación;"
'DoCmd.RunSQL (strQuery)
' falla con error

' bucle en los campos
Dim j As Integer
Dim ntot As Integer
i = 0
Dim rstDiff As DAO.Recordset
Set rstDiff = CurrentDb.OpenRecordset("Diffs")

'
' NOTA: Los test de tiempo indican que se tarda exactamente lo mismo, y parece mejor mostrar el avance en el form
' por seo deja la segunda opción
'

'--------------------------------------
' opción 1 crear consultas de inserción
'--------------------------------------
' el hacer currentdb.execute "INSERT INTO tabla ( fields,...) VALUES ( values ..) en un bucle"
' falla por desboramiento del strign values al concatenar los valores


' con la consulta de inserción de golpe no hay problemas
'Dim fldFrom As String
'Dim fldTo As String
'fldTo = ""
'For j = 1 To rstDiff.Fields.count - 1 '!!! Ojo saltamos el primer campo que es el autonumérico IDN
'    fldTo = fldTo & "[" & rstDiff.Fields(j).Name & "],"
'Next
'fldTo = Left(fldTo, Len(fldTo) - 1) ' quitar "," final

'fldFrom = ""
'For j = 0 To rst.Fields.count - 1
'    fldFrom = fldFrom & "Comparación.[" & rst.Fields(j).Name & "],"
'Next
'fldFrom = Left(fldFrom, Len(fldFrom) - 1) ' quitar "," final

'Set db = CurrentDb

'Add the records, being sure to use our db object, not CurrentDb
'db.Execute "INSERT INTO Diffs (" & fldTo & ")" & " SELECT  " & fldFrom & " FROM Comparación", dbFailOnError

'-------------------------------------
' OPción copiar uno a uno campo-valor
'------------------------------------
With rst
    If Not (rst.EOF And rst.BOF) Then
        rst.MoveLast
        rst.MoveFirst 'Unnecessary in this case, but still a good habit
        ntot = rst.RecordCount
        Do Until rst.EOF = True
            rstDiff.AddNew
            ' no hace falta gestión de nulos, ya que están pèrmitidos en la definición de la tabla Diffs
            ' además una asignación if null then valor ="" da error si el campo es de tipo numérico
            For j = 0 To rst.Fields.count - 1 ' la tabla tiene 1 campo más, el Id
                rstDiff.Fields(j + 1).Value = rst.Fields(j).Value
            Next
            rstDiff.Update
            rst.MoveNext
            i = i + 1
            If i Mod 50 = 0 Then
                textLog.SetFocus
                textLog.Text = "Procesando " & i & "/" & ntot
                DoEvents
            End If
        Loop
    End If
End With

rst.Close
rstDiff.Close
Set rst = Nothing
Set rstDiff = Nothing


DoCmd.SetWarnings True

'DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel9, "Comparación", CurrentProject.Path & "\comparar.xls"
Exit Sub
ERROR:
    MsgBox ("Ha ocurrido un error en la geeración de la tabla en access" & vbCrLf & vbCrLf & Err.Description & vbCrLf & vbCrLf)
    DoCmd.Hourglass False
    Exit Sub
End Sub


Private Sub rellenarExcelRst(xlSh As Object, rst As DAO.Recordset) 'late binding
'Private Sub rellenarExcelRst(xlSh As Excel.Worksheet, rst As DAO.Recordset)
Dim iCols As Integer
For iCols = 0 To rst.Fields.count - 1
 xlSh.Cells(1, iCols + 1).Value = rst.Fields(iCols).Name
 xlSh.Cells(1, iCols + 1).Interior.ColorIndex = 15
Next
' si el rst está vacío salimos, pues da errores
If rst.EOF Then
    xlSh.Cells(2, 1) = "NO SE HAN ENCONTRADO"
    Exit Sub
End If
rst.MoveLast
rst.MoveFirst

xlSh.Range("A2").CopyFromRecordset rst

'For iCols = 1 To rst.RecordCount
'    xlSh.Rows(iCols).RowHeight = xlSh.Rows(1).RowHeight
'Next
' igualamos ancho de todas las filas y ponemos autofiltro
'xlSh.Range("A2:A" & xlSh.Rows.count).RowHeight = xlSh.Rows(1).RowHeight
On Error Resume Next ' evitar error en algunas tablets en autofilter
xlSh.Range("A1").AutoFilter
xlSh.Cells.Select
xlSh.Cells.EntireColumn.AutoFit
xlSh.Cells.EntireRow.AutoFit

End Sub
Private Sub pintarExcel(xlSh As Object, rst As DAO.Recordset)
'Private Sub pintarExcel(xlSh As Excel.Worksheet, rst As DAO.Recordset)

Dim iCols As Integer ' xls empieza en 1
Dim iRows As Integer ' xls empieza en 1
For iCols = 0 To rst.Fields.count - 1
    If xlSh.Columns(iCols + 1).ColumnWidth > 50 Then xlSh.Columns(iCols + 1).ColumnWidth = 50 ' no tan anchas
    If Left(rst.Fields(iCols).Name, 4) = "Expr" And rst.Fields(iCols).Name <> "ExprTotal" Then ' estamos en una columna con valores de comparación
        xlSh.Cells(1, iCols + 1).Interior.ColorIndex = 34 ' otro azul
        For iRows = 1 To rst.RecordCount
            ' la primera fila son los campos
            If xlSh.Cells(iRows + 1, iCols + 1).Value < 1 Then
                xlSh.Cells(iRows + 1, iCols).Interior.ColorIndex = 6 ' o 27 , amarillos
                xlSh.Cells(iRows + 1, iCols - 1).Interior.ColorIndex = 6 ' o 27 , amarillos
            End If
            If iRows Mod 100 = 0 Then
                textLog.SetFocus
                textLog.Text = "Columna:" & iCols & " Fila: " & iRows & "/" & rst.RecordCount
                DoEvents
            End If
        Next
    End If
    If rst.Fields(iCols).Name = "ExprTotal" Then xlSh.Cells(1, iCols + 1).Interior.ColorIndex = 42 'azul
Next

End Sub
Private Sub rellenarExcel(xlSh As Object, rst As DAO.Recordset, row As Integer, comboCampo As String, comboTabla As String)
'Private Sub rellenarExcel(xlSh As Excel.Worksheet, rst As DAO.Recordset, row As Integer, comboCampo As String, comboTabla As String)
Dim col As Integer
Dim count As Integer
Dim i As Integer
Dim valor As Variant


If Not (rst.EOF And rst.BOF) Then
    rst.MoveLast
    rst.MoveFirst
    count = rst.RecordCount
    i = 1
    For Each valor In rst.Fields
        xlSh.Cells(row, i) = valor.Name
        ' paleta de colores https://www.excel-easy.com/vba/examples/background-colors.html
        xlSh.Cells(row, i).Interior.ColorIndex = 15 'gris
        i = i + 1
    Next
    row = row + 1
      
    Do Until rst.EOF
        i = 1
        For Each valor In rst.Fields
            If Not IsNull(valor.Value) Then
                xlSh.Cells(row, i) = valor.Value
            Else
                xlSh.Cells(row, i) = ""
            End If
            i = i + 1
        Next
        xlSh.Rows(row).RowHeight = xlSh.Rows(1).RowHeight
        If row Mod 50 = 0 Then
            textLog.Text = " Diifs, Fila: " & row & "/" & count
            DoEvents
        End If
    row = row + 1
    rst.MoveNext
    Loop
End If
xlSh.Range("A1").AutoFilter
xlSh.Cells.Select
xlSh.Cells.EntireColumn.AutoFit
xlSh.Cells.EntireRow.AutoFit
End Sub

Private Sub comboTablas1_Click()
Dim db As DAO.Database
Dim rst As DAO.Recordset
Dim comboName As Variant
If IsNull(comboTablas1) Or comboTablas1 = "" Then Exit Sub


'Limpiar combobox campos
While comboCampo1.ListCount > 0
    comboCampo1.RemoveItem 0
    For Each comboName In Split(camposCombo1, ",")
        Me.Controls(comboName).RemoveItem 0
    Next
Wend

comboCampo1 = ""
For Each comboName In Split(camposCombo1, ",")
        Me.Controls(comboName) = ""
Next
    
Set db = CurrentDb()
Set rst = db.OpenRecordset(comboTablas1)
Dim fld As DAO.Field
For Each fld In rst.Fields
    comboCampo1.AddItem (fld.Name)
    For Each comboName In Split(camposCombo1, ",")
        Me.Controls(comboName).AddItem (fld.Name)
    Next
Next
rst.Close
Set fld = Nothing
Set rst = Nothing
Set db = Nothing


End Sub

Private Sub comboTablas2_Click()
Dim db As DAO.Database
Dim rst As DAO.Recordset
Dim comboName As Variant

If IsNull(comboTablas2) Or comboTablas2 = "" Then Exit Sub


'Limpiar combobox
While comboCampo2.ListCount > 0
    comboCampo2.RemoveItem 0
    For Each comboName In Split(camposCombo2, ",")
        Me.Controls(comboName).RemoveItem 0
    Next
Wend

comboCampo2 = ""
For Each comboName In Split(camposCombo2, ",")
        Me.Controls(comboName) = ""
Next

Set db = CurrentDb()
Set rst = db.OpenRecordset(comboTablas2)
Dim fld As DAO.Field
For Each fld In rst.Fields
    comboCampo2.AddItem (fld.Name)
    For Each comboName In Split(camposCombo2, ",")
        Me.Controls(comboName).AddItem (fld.Name)
    Next
Next
rst.Close
Set fld = Nothing
Set rst = Nothing
Set db = Nothing


End Sub

Private Sub Form_Load()
Dim comboName As Variant
Set db = CurrentDb()
comboTablas1.RowSource = ""
comboCampo1.RowSource = ""
comboTablas2.RowSource = ""
comboCampo2.RowSource = ""

For Each comboName In Split(camposCombo1, ",")
        Me.Controls(comboName).RowSource = ""
Next
For Each comboName In Split(camposCombo2, ",")
        Me.Controls(comboName).RowSource = ""
Next

For Each td In db.TableDefs
    If Left(td.Name, 4) <> "MSys" And td.Name <> "RegExp" Then
        comboTablas1.AddItem (td.Name)
        comboTablas2.AddItem (td.Name)
    End If
Next td

CheckboxTabla1.Value = False
CheckboxTabla2.Value = False
checkBoxLimpiar.Value = False
checkBoxInOut.Value = False

Set td = Nothing
Set db = Nothing

End Sub

Private Sub quitarNulos(tablaName As String)
'https://stackoverflow.com/questions/29064325/replacing-null-values-for-multiple-columns-in-a-table-with-a-constant-value
Dim db As DAO.Database
Dim fld As DAO.Field
Dim tdf As DAO.TableDef

Dim strUpdate As String

Dim campoBusqueda As String
Dim camposBusqueda(20) As String
Dim i As Integer

Set db = CurrentDb
Set tdf = db.TableDefs(tablaName)

' Array con los campos de búsqueda, para comprobar si estamos en uno de ellos, en las tablas a comparar

' para la tabla1
i = 0
If tablaName = comboTablas1.Value Then
    i = 0
    For Each valor In Split(camposCombo1, ",")
        If Not IsNull(Me.Controls(valor)) Then
            camposBusqueda(i) = Me.Controls(valor)
        Else
            camposBusqueda(i) = ""
        End If
        i = i + 1
    Next
End If
' para la tabla 2
i = 0
If tablaName = comboTablas2.Value Then
    For Each valor In Split(camposCombo2, ",")
        If Not IsNull(Me.Controls(valor)) Then
            camposBusqueda(i) = Me.Controls(valor)
        Else
            camposBusqueda(i) = ""
        End If
        i = i + 1
    Next
End If


For Each fld In tdf.Fields
    ' Access will complain if you attempt an UPDATE on an autonumber field,
    ' so skip field with dbAutoIncrField attribute
    If Not ((fld.Attributes And dbAutoIncrField) = dbAutoIncrField) Then
        ' ver además si es un campo de búsqueda
        i = 0
        Do While camposBusqueda(i) <> ""
            If fld.Name = camposBusqueda(i) Then
                ' convertir a tipo texto
                If fld.Type <> dbText Then ' cazamos los números y los memo
                    MsgBox "Se va a cambiar el tipo de datos de " & fld.Name & " de la tabla " & tablaName & " a TEXTO", vbExclamation
                    db.Execute "ALTER TABLE [" & tablaName & "] ALTER COLUMN [" & fld.Name & "] TEXT;"
                End If
                ' cambiar si es nulo y poner cadena vacía
                    strUpdate = "UPDATE [" & tablaName & "] SET [" & fld.Name & _
                        "] = """" WHERE [" & fld.Name & "] Is Null;"
                    '   "] = -99999 WHERE [" & fld.Name & "] Is Null;"
                    'Debug.Print strUpdate
                    db.Execute strUpdate, dbFailOnError
            End If
        i = i + 1
        Loop
        
    End If
    
Next

End Sub





Leave a Reply

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.