none
Evitar que se dupliquen los registros en la base de datos. RRS feed

  • Pregunta

  • Hola muy buenas, necesito ayuda, tengo un boton en el cual lee e importa todos los registro a la base de datos de sql, lo tengo mediante un metodo, pero quisiera saber como puedo hacer que evite que se dupliquen los datos ignorandolos sin que se termine el proceso, la llame primaria es cedula, y por eso cuando ve dos iguales tira error de duplicados, lo que quiero es evitar que se de este error.

    Este es el codigo.

    Sub ExceltoSqlServer()
            Dim myfileDialog As New OpenFileDialog()
            Dim xSheet As String = ""


            With myfileDialog
                .Filter = "Excel files |*.xlsx"
                .Title = "Open File"
                .ShowDialog()

            End With
            If myfileDialog.FileName.ToString <> "" Then
                Dim ExcelFile As String = myfileDialog.FileName.ToString
                xSheet = InputBox("Digite el nombre de la hoja que desea importar", "Complete")
                conn = New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;" & "data source=" & ExcelFile & "; " & "Extended Properties='Excel 12.0 Xml;HDR=Yes'")

                Try
                    conn.Open()
                    da = New OleDbDataAdapter("SELECT * FROM [" & xSheet & "$]", conn)
                    ds = New DataSet
                    da.Fill(ds)
                    sqlBC = New SqlBulkCopy(msCadenaSQL)
                    sqlBC.DestinationTableName = "DATOS"
                    sqlBC.WriteToServer(ds.Tables(0))


                Catch ex As Exception
                    MsgBox("Error" + ex.ToString, MsgBoxStyle.Information, "Informacion")
                Finally
                    conn.Close()
                    MessageBox.Show("DATOS REGISTRADOS")
                End Try
            End If
        End Sub


    • Cambiado Enrique M. Montejo miércoles, 22 de noviembre de 2017 7:11 Pregunta relacionada con el acceso a datos.
    martes, 14 de noviembre de 2017 19:24

Respuestas

  • Revisa el siguiente código:

    Using Conn As New SqlConnection("Cadena de conexión")
    	Conn.Open()
    
    	'Crear tabla temporal con todas las columnas que se desea copiar
    	Dim consultaSQL As String = "CREATE TABLE #TempTable (Cedula, Col1, Col2, Col3, ...)"
    	Dim cmd = New SqlCommand(consultaSQL, Conn)
    	cmd.ExecuteNonQuery()
    
    	'Insertar datos en tabla temporal
    	Using bulkCopy As New SqlBulkCopy(Conn.ConnectionString)
    		bulkCopy.DestinationTableName = "#TempTable"
    		bulkCopy.WriteToServer(dt) 'dt contiene los datos que se desea importar
    	End Using
    
    	'Transferir de la tabla temporal a la tabla "destino" las filas no existentes 
    	cmd.CommandText = "INSERT INTO dbo.TableName (Cedula, Col1, Col2, Col3, ...) 
    		SELECT t.Cedula, t.Col1, t.Col2, t.Col3, ... FROM #TempTable t 
    		WHERE NOT EXISTS (SELECT 1 FROM dbo.TableName WHERE t.Cedula = Cedula); 
    		DROP TABLE #TempTable;"
    
    	cmd.ExecuteNonQuery()
    End Using

    De hecho es el mismo código que proporcioné en el hilo que abriste en el foro de SQL Server

    Crear una tabla temporal para evitar que se inserten archivos duplicados.

    • Marcado como respuesta Ruben_Mendoza martes, 21 de noviembre de 2017 17:54
    lunes, 20 de noviembre de 2017 23:21

Todas las respuestas

  • El SqlBulkCopy que estás usando tiene muy alto rendimiento, pero como contrapartida no tiene flexibilidad para poder hacer ese filtro de duplicados que necesitas.

    Habría una solución que sería añadir un trigger de tipo INSTEAD OF INSERT en la tabla, y que sea el trigger el que verifique la ausencia de duplicidad antes de insertar. Pero te va a salir un trigger bastante complicado y se va a cargar el rendimiento del SqlBulkCopy, así que no creo que merezca la pena seguir esta vía.

    Una alternativa es sustituir el SqlBulkCopy por un bucle que vaya recorriendo las filas del ds.Tables(0), y por cada fila haga un Select contra la base de datos, y si el registro no existe, que a continuación haga un Insert. Si quieres mejorar un poco el rendimiento, puedes combinar las dos cosas en una única sentencia del tipo "if not exists ... insert into ...", con lo que te ahorras la mitad de los viajes al servidor. También puedes hacer la insert directamente dentro de un try...catch y despreciar la excepción en caso de que te devuelva un error de duplicidad.

    Si necesariamente tienes que optimizar al máximo, reduciendo todo lo posible las llamadas al servidor, entonces puedes hacer el SqlBulkCopy contra una tabla temporal, en lugar de la tabla definitiva, y luego mandar ejecutar al servidor una sentencia MERGE que combine la tabla temporal sobre la tabla de destino.

    • Propuesto como respuesta Juan Mondragón martes, 14 de noviembre de 2017 22:17
    martes, 14 de noviembre de 2017 20:34
  • La verdad es que soy muy nuevo en esto, que me recomendarias hacer tu?
    miércoles, 15 de noviembre de 2017 14:18
  • Quiero saber si es posible crear un procedimiento que elimine todos los datos que esten duplicados en la base de datos.
    • Combinado Enrique M. Montejo sábado, 18 de noviembre de 2017 16:38 Preguntas relacionadas
    miércoles, 15 de noviembre de 2017 16:21
  • Si eres muy nuevo, probablemente la solucion mas sencilla (y ademas instructiva, porque asi aprendes a grabar con un Command) sea la de hacer un Insert dentro de un try...catch. El rendimiento sera bueno siempre que no haya muchos duplicados (que los duplicados sean la excepcion y no la norma).

    Mas o menos quedara algo parecido a lo siguiente, pero esta muy simplificado, tendras que completarlo con todos los campos que hayan de intervenir en la Insert:

                Try
                     conn.Open()
                     da = New OleDbDataAdapter("SELECT * FROM [" & xSheet & "$]", conn)
                     dt = New DataTable
                     da.Fill(dt)
    
                     Using SqlConn as New SqlConnection(msCadenaSQL)
                         Dim cmd as New SqlCommand("Insert into DATOS(campo1, campo2, etc) values (@campo1, @campo2, etc)", SqlConn)
                         cmd.Parameters.Add("@campo1", SqlDbType.Varchar, 100) 'Ojo, ajusta el tipo y longitud segun el campo de la base de datos
                         cmd.Parameters.Add("@campo2", SqlDbType.Int) 'Ojo, ajusta el tipo y longitud si procede segun el campo de la base de datos
                         For Each dr as DataRow in dt.Rows
                             cmd.Parameters(0).Value = dr("Campo1") 'Ojo, puede ser que aqui necesites convertir el tipo de dato si no coincide el tipo que hay en Excel con el tipo que hay que grabar en SQL Server
                             cmd.Parameters(1).Value = dr("Campo2")
                             'Recordar que hay que agregar el resto de los campos de manera similar
                             Try
                                 cmd.ExecuteNonQuery()
                             Catch ex As LoQueSeaException 'Verifica cual es el tipo de excepcion que ocurre cuando hay un duplicado y ponlo aqui
                                 'No hacemos nada, despreciamos esta excepcion. Aqui podrias grabar un "Log" con los duplicados.
                             End Try
                         Next
                     End Using
                 Catch ex As Exception
                     MsgBox("Error" + ex.ToString, MsgBoxStyle.Information, "Informacion")
                 Finally
                     conn.Close()
                     MessageBox.Show("DATOS REGISTRADOS")
                 End Try





    miércoles, 15 de noviembre de 2017 16:45
  • Pues depende del tipo de base de datos del que esté hablando y de los campos que quieras controlar como no duplicados.

    Puedes crear una tabla temporal en la que incluyas solo los registros no duplicados y copiar esos registros en la tabla original.


    Saludos, Javier J

    miércoles, 15 de noviembre de 2017 17:02
  • Que deseas hacer:

    1. Eliminar registros duplicados en una tabla

    2. Evitar la inserción de duplicados desde VB.net a una tabla

    Saludos.


    Heytel Martinez

    miércoles, 15 de noviembre de 2017 17:16
  • Que deseas hacer:

    1. Eliminar registros duplicados en una tabla

    2. Evitar la inserción de duplicados desde VB.net a una tabla

    Saludos.


    Heytel Martinez

    necesito el numero 2
    miércoles, 15 de noviembre de 2017 17:39
  • Estoy trabajando en sql, y los campos son la id
    miércoles, 15 de noviembre de 2017 17:40
  • Si quieres evitar duplicados antes de grabar el registro tienes que comprobar si ya existe otro con esa Id en la tabla con una consulta del tipo

    Public Function ExisteRegistro(ByVal Id As Integer) As Boolean
        Using conn As New SqlConnection("CadenaConexion")
            Dim query = "SELECT COUNT(*) FROM TABLA WHERE Id=@Id"
            Dim cmd As New SqlCommand(query, conn)
            cmd.Parameters.AddWithValue("@Id", Id)
            conn.Open()
     
            Dim count As Integer = Convert.ToInt32(cmd.ExecuteScalar())
            If count = 0 Then
                Return False
            Else
                Return True
            End If
        End Using
    End Function


    Saludos, Javier J

    • Propuesto como respuesta HRMA miércoles, 15 de noviembre de 2017 19:38
    miércoles, 15 de noviembre de 2017 17:53
  • Hola:
    Lo mejor para evitar duplicados es que esta "labor" la ejecute el motor de base de datos.
    Para eso tienes que diseñar bien la tabla con sus PK, IK, FK etc

    Un saludo desde Bilbo
    Carlos
    jueves, 16 de noviembre de 2017 9:21
  • Hola:
    Lo mejor para evitar duplicados es que esta "labor" la ejecute el motor de base de datos.
    Para eso tienes que diseñar bien la tabla con sus PK, IK, FK etc

    Un saludo desde Bilbo
    Carlos
    Si ya tengo las pk asignadas que en este caso son las cedulas, pero es que tengo un boton que importa todos los datos de un excel a la base de datos, pero si un dato esta repetido termina el proceso y lanza error de duplicados.
    jueves, 16 de noviembre de 2017 15:06
  • Si quieres evitar duplicados antes de grabar el registro tienes que comprobar si ya existe otro con esa Id en la tabla con una consulta del tipo

    Public Function ExisteRegistro(ByVal Id As Integer) As Boolean
        Using conn As New SqlConnection("CadenaConexion")
            Dim query = "SELECT COUNT(*) FROM TABLA WHERE Id=@Id"
            Dim cmd As New SqlCommand(query, conn)
            cmd.Parameters.AddWithValue("@Id", Id)
            conn.Open()
     
            Dim count As Integer = Convert.ToInt32(cmd.ExecuteScalar())
            If count = 0 Then
                Return False
            Else
                Return True
            End If
        End Using
    End Function


    Saludos, Javier J

    Gracias intentare emplear ese codigo y adaptarlo.

    jueves, 16 de noviembre de 2017 15:06
  • Si eres muy nuevo, probablemente la solucion mas sencilla (y ademas instructiva, porque asi aprendes a grabar con un Command) sea la de hacer un Insert dentro de un try...catch. El rendimiento sera bueno siempre que no haya muchos duplicados (que los duplicados sean la excepcion y no la norma).

    Mas o menos quedara algo parecido a lo siguiente, pero esta muy simplificado, tendras que completarlo con todos los campos que hayan de intervenir en la Insert:

                Try
                     conn.Open()
                     da = New OleDbDataAdapter("SELECT * FROM [" & xSheet & "$]", conn)
                     dt = New DataTable
                     da.Fill(dt)
    
                     Using SqlConn as New SqlConnection(msCadenaSQL)
                         Dim cmd as New SqlCommand("Insert into DATOS(campo1, campo2, etc) values (@campo1, @campo2, etc)", SqlConn)
                         cmd.Parameters.Add("@campo1", SqlDbType.Varchar, 100) 'Ojo, ajusta el tipo y longitud segun el campo de la base de datos
                         cmd.Parameters.Add("@campo2", SqlDbType.Int) 'Ojo, ajusta el tipo y longitud si procede segun el campo de la base de datos
                         For Each dr as DataRow in dt.Rows
                             cmd.Parameters(0).Value = dr("Campo1") 'Ojo, puede ser que aqui necesites convertir el tipo de dato si no coincide el tipo que hay en Excel con el tipo que hay que grabar en SQL Server
                             cmd.Parameters(1).Value = dr("Campo2")
                             'Recordar que hay que agregar el resto de los campos de manera similar
                             Try
                                 cmd.ExecuteNonQuery()
                             Catch ex As LoQueSeaException 'Verifica cual es el tipo de excepcion que ocurre cuando hay un duplicado y ponlo aqui
                                 'No hacemos nada, despreciamos esta excepcion. Aqui podrias grabar un "Log" con los duplicados.
                             End Try
                         Next
                     End Using
                 Catch ex As Exception
                     MsgBox("Error" + ex.ToString, MsgBoxStyle.Information, "Informacion")
                 Finally
                     conn.Close()
                     MessageBox.Show("DATOS REGISTRADOS")
                 End Try





    Ya adapte todo el codigo solo que esta parte que marque en negrita me dice que "msCadena" no se puede convertir en tipo string, eso es lo que falta para poder probarlo, ya intente cambiar hasta la sqlconecction y nada.
    jueves, 16 de noviembre de 2017 15:30
  • ...tengo un botón que importa todos los datos de un Excel a la base de datos, pero si un dato está repetido termina el proceso y lanza error de duplicados.

    Hay un avance respecto al contexto del caso, de hecho, todos los alcances que has dado anteriormente han sido ambiguos y hasta contradictorios, te recomiendo presentar desde un inicio el desarrollo de tu requerimiento (caso, dudas, ejemplos, prueba-error) de manera que podamos presentarte una solución de acuerdo a lo que requieres y evitar especulaciones.

    No dejas claro donde se encuentran los datos duplicados, ¿en el origen de datos (archivo en Microsoft Excel)? o ¿en la tabla de la la base de datos debido a importaciones realizadas anteriormente?

    - Si los datos duplicados se encuentra en el origen de los datos podrías evitar las "filas iguales" desde la consulta de selección

    da = New OleDbDataAdapter("SELECT DISTINCT Col1, Col2, Col3 FROM [" & xSheet & "$]", conn)

    - Si los datos duplicados se encuentra en la tabla de base de datos podrías realizar una inserción condicionada, por ejemplo:

    INSERT INTO TableName (Cedula, Col1, Col2)
    SELECT o.Cedula, o.Col1, o.Col2 
    FROM Origen o WHERE NOT EXISTS (SELECT 1 FROM TableName WHERE Cedula = o.Cedula);
    GO

    • Propuesto como respuesta Pablo Rubio viernes, 17 de noviembre de 2017 15:58
    jueves, 16 de noviembre de 2017 16:04
  • ...tengo un botón que importa todos los datos de un Excel a la base de datos, pero si un dato está repetido termina el proceso y lanza error de duplicados.

    Hay un avance respecto al contexto del caso, de hecho, todos los alcances que has dado anteriormente han sido ambiguos y hasta contradictorios, te recomiendo presentar desde un inicio el desarrollo de tu requerimiento (caso, dudas, ejemplos, prueba-error) de manera que podamos presentarte una solución de acuerdo a lo que requieres y evitar especulaciones.

    No dejas claro donde se encuentran los datos duplicados, ¿en el origen de datos (archivo en Microsoft Excel)? o ¿en la tabla de la la base de datos debido a importaciones realizadas anteriormente?

    - Si los datos duplicados se encuentra en el origen de los datos podrías evitar las "filas iguales" desde la consulta de selección

    da = New OleDbDataAdapter("SELECT DISTINCT Col1, Col2, Col3 FROM [" & xSheet & "$]", conn)

    - Si los datos duplicados se encuentra en la tabla de base de datos podrías realizar una inserción condicionada, por ejemplo:

    INSERT INTO TableName (Cedula, Col1, Col2)
    SELECT o.Cedula, o.Col1, o.Col2 
    FROM Origen o WHERE NOT EXISTS (SELECT 1 FROM TableName WHERE Cedula = o.Cedula);
    GO

    Sub ExceltoSqlServer()
            Dim myfileDialog As New OpenFileDialog()
            Dim xSheet As String = ""


            With myfileDialog
                .Filter = "Excel files |*.xlsx"
                .Title = "Open File"
                .ShowDialog()

            End With
            If myfileDialog.FileName.ToString <> "" Then
                Dim ExcelFile As String = myfileDialog.FileName.ToString
                xSheet = InputBox("Digite el nombre de la hoja que desea importar", "Complete")
                conn = New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;" & "data source=" & ExcelFile & "; " & "Extended Properties='Excel 12.0 Xml;HDR=Yes'")

                Try
                    conn.Open()
                    da = New OleDbDataAdapter("SELECT * FROM [" & xSheet & "$]", conn)
                    ds = New DataSet
                    da.Fill(ds)
                    sqlBC = New SqlBulkCopy(msCadenaSQL)
                    sqlBC.DestinationTableName = "DATOS"
                    sqlBC.WriteToServer(ds.Tables(0))


                Catch ex As Exception
                    MsgBox("Error" + ex.ToString, MsgBoxStyle.Information, "Informacion")
                Finally
                    conn.Close()
                    MessageBox.Show("DATOS REGISTRADOS")
                End Try
            End If
        End Sub

    Este es el codigo que estoy utilizando, todo el excel despues lo importa, pero si en tal caso hay un datos que ya este introducido en la base de datos, lanza un error y termina el proceso, ya que tengo la PK el dato "CEDULA" que no puede estar repetida, lo que quiero hacer es que no se termine el proceso y por decirlo asi que no agregue el dato duplicado, y siga con los demas, asi constantemente. No se si me di a entender.

    Saludes.

    jueves, 16 de noviembre de 2017 16:11
  • Ahi lo que hace falta es abrir un SqlConnection para poder utilizarlo. Supuse que tu cadena de conexion seria msCadenaSQL juzgando por el nombre y por el hecho de que la usabas en el SqlBulkCopy. Por eso the puse un "New sqlconnection" presumiendo que en esa variable estaba la cadena. Por cierto, en mi ejemplo se me olvido meter el SqlConn.Open(), me imagino que ya te habras dado cuenta y lo habras corregido.

    Si en tu programa estas manejando las conexiones de alguna manera distinta, ponnos aqui como las manejas (donde tienes la cadena de conexion y donde abres y cierras las conexiones), y adaptamos este codigo para que use los mismos mecanismos y/o las mismas variables que estes usando para las conexiones a base de datos.

    jueves, 16 de noviembre de 2017 17:01
  • No habia notado eso de sqlcoon, pero ya lo agregue y si mi cadena de conexion es esta  

     Public msCadenaSQL As SqlConnection

    De igual manera me sigue mostrando el mismo error.

    jueves, 16 de noviembre de 2017 17:15
  • No habia notado eso de sqlcoon, pero ya lo agregue y si mi cadena de conexion es esta  

     Public msCadenaSQL As SqlConnection

    De igual manera me sigue mostrando el mismo error.

    Public dt As New DataTable
        Public da As OleDbDataAdapter
        Public conn As OleDbConnection
        Public configuracion As ConnectionStringSettings = ConfigurationManager.ConnectionStrings("msCadenaSQL")
        Public msCadenaSQL As SqlConnection
        Public sqlBC As SqlBulkCopy
    jueves, 16 de noviembre de 2017 17:17
  • Ah, segun eso, tu cadena esta en "configuracion", mientras que en cambio lo que has llamado "msCadenasql" no es la cadena sino que es ya el propio SqlConnection, aunque no se ve por ningun sitio si lo inicializas o no lo inicializas antes de llamar a ExceltoSqlServer.

    Suponiendo que NO tienes ya un SqlConnection inicializado, la forma de inicializarlo es la que te puse, pero cambiando la variable por la cadena tomada de "configuracion":

    Using SqlConn as New SqlConnection(configuracion.ConnectionString)
           SqlConn.Open()

    Aprovecho la ocasion para destacar de esta experiencia lo importante que es elegir nombres correctos para las variables. Si pones "cadena" en el nombre de algo que no es la cadena, pues es bastante logico que quien lea tu codigo se confunda y use esa variable en donde no hay que usarla. Y si le llamas a algo "configuracion", que "no dice nada" (podria referirsa a muchas cosas), pues igualmete hace poco legible el codigo.

    jueves, 16 de noviembre de 2017 17:30
  • Ah, segun eso, tu cadena esta en "configuracion", mientras que en cambio lo que has llamado "msCadenasql" no es la cadena sino que es ya el propio SqlConnection, aunque no se ve por ningun sitio si lo inicializas o no lo inicializas antes de llamar a ExceltoSqlServer.

    Suponiendo que NO tienes ya un SqlConnection inicializado, la forma de inicializarlo es la que te puse, pero cambiando la variable por la cadena tomada de "configuracion":

    Using SqlConn as New SqlConnection(configuracion.ConnectionString)
           SqlConn.Open()

    Aprovecho la ocasion para destacar de esta experiencia lo importante que es elegir nombres correctos para las variables. Si pones "cadena" en el nombre de algo que no es la cadena, pues es bastante logico que quien lea tu codigo se confunda y use esa variable en donde no hay que usarla. Y si le llamas a algo "configuracion", que "no dice nada" (podria referirsa a muchas cosas), pues igualmete hace poco legible el codigo.

    Amigo muchas gracias, ya me sirvio a la perfeccion, pero esto tendra algun problema cuando sean miles de datos?

    por ultimo y para terminar, como podria hacer para que me muestre cuantos datos agrego, o sea la cantidad.

    Y tendre muy presente eso de las varibles, gracias.

    jueves, 16 de noviembre de 2017 17:44
  • [...]pero esto tendra algun problema cuando sean miles de datos?

    No. Tendrá problema cuando sean millones. Con unos pocos miles se los tragará sin ninguna dificultad.

    como podria hacer para que me muestre cuantos datos agrego, o sea la cantidad

    Disstingamos: Los datos que intentó agregar son dt.Rows.Count. Los datos que consiguió agregar habría que contarlos dentro del Try: Declara una variable de tipo Integer e inicialízala a cero. Debajo del ExecuteNonQuery, súmale 1. A la salida del bucle la variable contendrá el número de los registros que se insertaron.

    jueves, 16 de noviembre de 2017 22:06
  • Hola:
    Para este ejemplo he creado un libro de excel como el de la imagen

    He creado una tabla en SQLServer como la de la imagen

    En un Form como el de la imagen

    copia y pega el siguiente codigo

    Option Strict On
    Option Explicit On
    Imports System.Data.OleDb
    Imports System.Data.SqlClient
    Public Class Form1
        Private Existentes As New List(Of Integer)
        Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
            Me.DGVInicial.AllowUserToAddRows = False
            Me.DGVFinal.AllowUserToAddRows = False
            Me.btnCargar.Enabled = True
            Me.btnVisualizar.Enabled = False
            Try
                'Cargar el Datatable con el fichero XLSX y mostrarlo en DGVInicial
                Dim lsCadenaExcel As String = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=c:\borrar\libro1.xlsx;Extended Properties='Excel 12.0 Xml;HDR=Yes'"
                Using loConexionExcel As New OleDbConnection(lsCadenaExcel)
                    Dim lsQuery As String = "Select * From [Hoja1$]"
                    Using loDataAdapter As New OleDbDataAdapter(lsQuery, loConexionExcel)
                        Dim ldtDataTable As New DataTable
                        loDataAdapter.Fill(ldtDataTable)
                        Me.DGVInicial.DataSource = ldtDataTable
                    End Using
                End Using
                'Cargar la lista Existentes con los registros que hay en la tabla prueba para consultar si ya existe
                Dim lsCadenaSQL As String = "Data Source=.\SQLEXPRESS;Initial Catalog=TU_BASE_DE_DATOS;Integrated Security=True"
                Using loConexionSQL As New SqlConnection(lsCadenaSQL)
                    Dim lsQuery As String = "Select * From prueba"
                    Dim ldtDataTable As New DataTable
                    Using loComando As New SqlCommand(lsQuery, loConexionSQL)
                        Using loDataAdapter As New SqlDataAdapter(lsQuery, loConexionSQL)
                            loDataAdapter.Fill(ldtDataTable)
                        End Using
                        For Each Fila As DataRow In ldtDataTable.Rows
                            Existentes.Add(CInt(Fila.Item("id")))
                        Next
                    End Using
                End Using
                Me.lblIni.Text = Me.DGVInicial.RowCount.ToString
            Catch ex As Exception
                MessageBox.Show(ex.Message, "Load", MessageBoxButtons.OK, MessageBoxIcon.Information)
            End Try
        End Sub
        Private Sub btnCargar_Click(sender As Object, e As EventArgs) Handles btnCargar.Click
            Try
                'Iniciamos una transaccion; hay que añadir la Referencia System.Transactions
                Using MiTransaccion As New Transactions.TransactionScope()
                    Dim lsCadenaSQL As String = "Data Source=.\SQLEXPRESS;Initial Catalog=TU_BASE_DE_DATOS;Integrated Security=True"
                    Using loConexionSQL As New SqlConnection(lsCadenaSQL)
                        loConexionSQL.Open()
                        Dim lsQuery As String = "Insert Into prueba (id) Values (@Id)"
                        For Each Fila As DataGridViewRow In Me.DGVInicial.Rows
                            'Si esta en la lista Existentes, no hacer nada, si NO esta, añadirlo a la lista y a la tabla
                            If Existentes.Contains(CInt(Fila.Cells("id").Value)) = False Then
                                Existentes.Add(CInt(Fila.Cells("id").Value))
                                Using loComando As New SqlCommand(lsQuery, loConexionSQL)
                                    loComando.Parameters.Add(New SqlParameter("@Id", CInt(Fila.Cells("id").Value)))
                                    loComando.ExecuteNonQuery()
                                End Using
                            End If
                        Next
                    End Using
                    MiTransaccion.Complete()
                    Me.btnCargar.Enabled = False
                    Me.btnVisualizar.Enabled = True
                End Using
            Catch ex As Exception
                MessageBox.Show(ex.Message, "Cargar", MessageBoxButtons.OK, MessageBoxIcon.Information)
            End Try
        End Sub
        Private Sub btnVisualizar_Click(sender As Object, e As EventArgs) Handles btnVisualizar.Click
            Try
                Dim lsCadenaSQL As String = "Data Source=.\SQLEXPRESS;Initial Catalog=TU_BASE_DE_DATOS;Integrated Security=True"
                Using loConexionSQL As New SqlConnection(lsCadenaSQL)
                    Dim lsQuery As String = "Select * From prueba"
                    Using loDataAdapter As New SqlDataAdapter(lsQuery, loConexionSQL)
                        Dim ldtDataTable As New DataTable
                        loDataAdapter.Fill(ldtDataTable)
                        Me.DGVFinal.DataSource = ldtDataTable
                    End Using
                End Using
                Me.lblFin.Text = Me.DGVFinal.RowCount.ToString
            Catch ex As Exception
                MessageBox.Show(ex.Message, "Visualizar", MessageBoxButtons.OK, MessageBoxIcon.Information)
            End Try
        End Sub
    End Class

    P.D.
    Espero que te sirva

    Un saludo desde Bilbo
    Carlos


    • Editado J. Carlos Herrero viernes, 17 de noviembre de 2017 5:40
    • Propuesto como respuesta Pablo Rubio viernes, 17 de noviembre de 2017 15:47
    viernes, 17 de noviembre de 2017 5:36
  • Hola:
    Mira el siguiente enlace

    https://social.msdn.microsoft.com/Forums/es-ES/09cc626e-ed32-40ca-b4c9-1a240ba66775/evitar-que-se-dupliquen-los-registros-en-la-base-de-datos?forum=vbes

    Un saludo desde Bilbo
    Carlos

    viernes, 17 de noviembre de 2017 5:55
  • [...]pero esto tendra algun problema cuando sean miles de datos?

    No. Tendrá problema cuando sean millones. Con unos pocos miles se los tragará sin ninguna dificultad.

    como podria hacer para que me muestre cuantos datos agrego, o sea la cantidad

    Disstingamos: Los datos que intentó agregar son dt.Rows.Count. Los datos que consiguió agregar habría que contarlos dentro del Try: Declara una variable de tipo Integer e inicialízala a cero. Debajo del ExecuteNonQuery, súmale 1. A la salida del bucle la variable contendrá el número de los registros que se insertaron.

    es que digamos estoy intentando pasar al menos unos 200 mil datos, pero me da error de sesion y no sé a que se deba ese error, o sea como que se corta la conexion.
    viernes, 17 de noviembre de 2017 14:07
  • Esta bien, voy a intentarlo.
    viernes, 17 de noviembre de 2017 14:08

  • Si necesariamente tienes que optimizar al máximo, reduciendo todo lo posible las llamadas al servidor, entonces puedes hacer el SqlBulkCopy contra una tabla temporal, en lugar de la tabla definitiva, y luego mandar ejecutar al servidor una sentencia MERGE que combine la tabla temporal sobre la tabla de destino.

    Hola amigo, me podria ayudar a hacer este procedimiento es que la verdad no sé como y el otro con el que me ayudaste, me da un error de sesion y necesita demasiada memoria.
    lunes, 20 de noviembre de 2017 15:09
  • [...] SqlBulkCopy contra una tabla temporal, en lugar de la tabla definitiva, y luego mandar ejecutar al servidor una sentencia MERGE [...]

    Hola amigo, me podria ayudar a hacer este procedimiento

    Si eres muy "nuevo", y ya el otro metodo te costaba trabajo, probablemente con este te vas a perder. Pero de todas maneras, te indico los pasos por si lo quieres intentar:

    1) Crea en el servidor una tabla temporal. En SQL Server esto se consigue poniendole en el nombre el prefijo "#", por ejemplo, "create table #MiTabla (...)". En el create table tendras que poner todos los mismos campos que tiene tu tabla de destino, pero sin agregar ningun indice ni constraint, ya que no son necesarios para este proceso. hay que escribir la sentencia create table usando la sintaxis de SQL Server, y mandarla ejecutar con el ExecuteNonQuery de un SqlCommand. Hay que mantener la misma conexion abierta durante todo el proceso porque al cerrarla se borra la tabla temporal.

    2) Inserta los registros en la tabla temporal usando el SqlBulkCopy de la misma manera que lo usabas en el codigo que pusiste originalmente en la primera pregunta. Primero cerciorate de que funciona correctamente, probandolo (sin duplicados) sobre la tabla "autentica". Una vez comprobado que funciona, cambia el nombre de tabla para que inserte sobre la tabla temporal creada en el paso 1.

    3) Utiliza de nuevo un ExecuteNonQuery para mandar ejecutar contra el servidor una sentencia MERGE. Sera mas o menos algo asi como "MERGE INTO laTablaFinal AS destino USING #MiTabla AS origen ON destino.CampoClave=Origen.CampoClave WHEN NOT MATCHED THEN INSERT(listaDeCampos) VALUES(origen.ListaDeCampos)". Esto solo copiara los no-duplicados, porque no hemos incluido la clausula "WHEN MATCHED" que seria la que procesaria los duplicados. Para escribir completa la sentencia MERGE tendras que estudiar la documentacion que esta aqui:

    https://docs.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql

    Te recomiendo primero ensayar escribiendo la sentencia en Management Studio y probandola a mano hasta que hayas verificado que todo funciona bien, antes de intentar meterla dentro de tu programa.

    4) Es buena costumbre borrar expresamente la tabla temporal, en lugar de esperar a que se borre por si sola al cerrar la conexion: "DROP TABLE #MiTabla" (una vez mas con un ExecuteNonquery).

    5) Ahora ya se puede hacer el Close() de la conexion, puesto que ya no necesitamos la tabla temporal.

     


    lunes, 20 de noviembre de 2017 17:10
  • [...] SqlBulkCopy contra una tabla temporal, en lugar de la tabla definitiva, y luego mandar ejecutar al servidor una sentencia MERGE [...]

    Hola amigo, me podria ayudar a hacer este procedimiento

    Si eres muy "nuevo", y ya el otro metodo te costaba trabajo, probablemente con este te vas a perder. Pero de todas maneras, te indico los pasos por si lo quieres intentar:

    1) Crea en el servidor una tabla temporal. En SQL Server esto se consigue poniendole en el nombre el prefijo "#", por ejemplo, "create table #MiTabla (...)". En el create table tendras que poner todos los mismos campos que tiene tu tabla de destino, pero sin agregar ningun indice ni constraint, ya que no son necesarios para este proceso. hay que escribir la sentencia create table usando la sintaxis de SQL Server, y mandarla ejecutar con el ExecuteNonQuery de un SqlCommand. Hay que mantener la misma conexion abierta durante todo el proceso porque al cerrarla se borra la tabla temporal.

    2) Inserta los registros en la tabla temporal usando el SqlBulkCopy de la misma manera que lo usabas en el codigo que pusiste originalmente en la primera pregunta. Primero cerciorate de que funciona correctamente, probandolo (sin duplicados) sobre la tabla "autentica". Una vez comprobado que funciona, cambia el nombre de tabla para que inserte sobre la tabla temporal creada en el paso 1.

    3) Utiliza de nuevo un ExecuteNonQuery para mandar ejecutar contra el servidor una sentencia MERGE. Sera mas o menos algo asi como "MERGE INTO laTablaFinal AS destino USING #MiTabla AS origen ON destino.CampoClave=Origen.CampoClave WHEN NOT MATCHED THEN INSERT(listaDeCampos) VALUES(origen.ListaDeCampos)". Esto solo copiara los no-duplicados, porque no hemos incluido la clausula "WHEN MATCHED" que seria la que procesaria los duplicados. Para escribir completa la sentencia MERGE tendras que estudiar la documentacion que esta aqui:

    https://docs.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql

    Te recomiendo primero ensayar escribiendo la sentencia en Management Studio y probandola a mano hasta que hayas verificado que todo funciona bien, antes de intentar meterla dentro de tu programa.

    4) Es buena costumbre borrar expresamente la tabla temporal, en lugar de esperar a que se borre por si sola al cerrar la conexion: "DROP TABLE #MiTabla" (una vez mas con un ExecuteNonquery).

    5) Ahora ya se puede hacer el Close() de la conexion, puesto que ya no necesitamos la tabla temporal.

     


     ok gracias por tu tiempo de ante mano, ya cree la tabla temporal, con las mismas columnas que la autentica, tambien ya hice el paso dos, el bulkcopy funciona correctamente, lo que no entendi fue eso que marque en negrita.
    lunes, 20 de noviembre de 2017 17:39
  • y es que en codigo pasado que me ayudaste, se me cerraba la sesion de proceso, no se a que se deba eso.

    Todo lo habia implementado bien, servia y todo solo que me daba ese inconveniente.

    lunes, 20 de noviembre de 2017 17:51
  • lo que no entendi fue eso que marque en negrita.

    En negrita dice a) que hay que crear la tabla temporal con SQL y usando ExecuteNonQuery. Pero, ¿no decías en tu respuesta que ya la habías creado? ¿Cómo has conseguido que tu programa cree la tabla al ejecutarlo, si no lo has hecho con SQL y con ExecuteNonQuery?

    b) Que hay que mantener la conexión abierta. Recuerda que una tabla temporal (cuyo nombre comienza por "#") solo sobrevive mientras está abierta la conexión que la creó, y se borra automáticamente al cerrar la conexión. De ahí la observación de que todo este proceso hay que hacerlo usando una sola conexión y no cerrarla hasta el final, porque si la cierras entre medias la tabla se borrará y ya no podrás realizar los siguientes pasos sobre esa tabla.


    lunes, 20 de noviembre de 2017 19:01
  • AH ok perdon mira esta esta es la creacion de la tabla temporal del executenonquery

    Dim cmd As New SqlCommand("create table #Cedulas(CODIGO INT  NOT NULL,PROVINCIA VARCHAR (45) NOT NULL,CANTON VARCHAR (45) NOT NULL,
                                                [DISTRITO ELECTORAL] VARCHAR (45) NOT NULL,CEDULA INT NOT NULL,[1ER APELLIDO] VARCHAR (45) NOT NULL,
                                                [2DO APELLIDO] VARCHAR (45) NOT NULL,NOMBRE VARCHAR (45) NOT NULL,SEXO INT NOT NULL,FECH_NAC INT NOT NULL)", msCadenaSQL)
        cmd.ExecuteNonQuery()

    Entonces suponiendo que el msConsulta.close() seria al final de todo el proceso no ?

    No se si voy bien.

    lunes, 20 de noviembre de 2017 19:15

  • 2) Inserta los registros en la tabla temporal usando el SqlBulkCopy de la misma manera que lo usabas en el codigo que pusiste originalmente en la primera pregunta. Primero cerciorate de que funciona correctamente, probandolo (sin duplicados) sobre la tabla "autentica". Una vez comprobado que funciona, cambia el nombre de tabla para que inserte sobre la tabla temporal creada en el paso 1.

    3) Utiliza de nuevo un ExecuteNonQuery para mandar ejecutar contra el servidor una sentencia MERGE. Sera mas o menos algo asi como "MERGE INTO laTablaFinal AS destino USING #MiTabla AS origen ON destino.CampoClave=Origen.CampoClave WHEN NOT MATCHED THEN INSERT(listaDeCampos) VALUES(origen.ListaDeCampos)". Esto solo copiara los no-duplicados, porque no hemos incluido la clausula "WHEN MATCHED" que seria la que procesaria los duplicados. Para escribir completa la sentencia MERGE tendras que estudiar la documentacion que esta aqui:

    https://docs.microsoft.com/en-us/sql/t-sql/statements/merge-transact-sql


    Supongo que lo que tenia que cambiar era donde decia "DATOS" en mi bulkcopy por "#Cedulas" que es mi tabla temporal, el bulkcopy funciona bien.

    Despues de pasar los datos a la tabla vendria el MERGE no?

    lunes, 20 de noviembre de 2017 19:21
  • Revisa el siguiente código:

    Using Conn As New SqlConnection("Cadena de conexión")
    	Conn.Open()
    
    	'Crear tabla temporal con todas las columnas que se desea copiar
    	Dim consultaSQL As String = "CREATE TABLE #TempTable (Cedula, Col1, Col2, Col3, ...)"
    	Dim cmd = New SqlCommand(consultaSQL, Conn)
    	cmd.ExecuteNonQuery()
    
    	'Insertar datos en tabla temporal
    	Using bulkCopy As New SqlBulkCopy(Conn.ConnectionString)
    		bulkCopy.DestinationTableName = "#TempTable"
    		bulkCopy.WriteToServer(dt) 'dt contiene los datos que se desea importar
    	End Using
    
    	'Transferir de la tabla temporal a la tabla "destino" las filas no existentes 
    	cmd.CommandText = "INSERT INTO dbo.TableName (Cedula, Col1, Col2, Col3, ...) 
    		SELECT t.Cedula, t.Col1, t.Col2, t.Col3, ... FROM #TempTable t 
    		WHERE NOT EXISTS (SELECT 1 FROM dbo.TableName WHERE t.Cedula = Cedula); 
    		DROP TABLE #TempTable;"
    
    	cmd.ExecuteNonQuery()
    End Using

    De hecho es el mismo código que proporcioné en el hilo que abriste en el foro de SQL Server

    Crear una tabla temporal para evitar que se inserten archivos duplicados.

    • Marcado como respuesta Ruben_Mendoza martes, 21 de noviembre de 2017 17:54
    lunes, 20 de noviembre de 2017 23:21
  • Despues de pasar los datos a la tabla vendria el MERGE no?

    Efectivamente, despues vendria el MERGE, despues borrar la tabla temporal, y finalmente cerrar la conexion.

    Fijate en el ejemplo que te ha puesto Williams Morales en otra respuesta. La unica diferencia es que ha usado un "insert... select... where..." que basicamente hace lo mismo que la sentencia Merge. En teoria se supone que la sentencia Merge es "mas inteligente" y puede resolver la combinacion con menos operaciones, pero para un caso tan sencillo como es este, sospecho que el optimizador de consultas resolvera ambos tipos de sentencia con la misma eficacia, asi que te dara un poco igual usar el insert o el merge, usa la variante que te resulte mas clara.

    martes, 21 de noviembre de 2017 12:05
  • Gracias ya lo implemente pero siento que me falta algo, ya  que no me copia las filas del excel, mira asi fue como quedo.

       

    Sub ExceltoSqlServer()
            Dim myfileDialog As New OpenFileDialog()
            Dim xSheet As String = ""


            With myfileDialog
                .Filter = "Excel files |*.xlsx"
                .Title = "Open File"
                .ShowDialog()

            End With
            If myfileDialog.FileName.ToString <> "" Then
                Dim ExcelFile As String = myfileDialog.FileName.ToString
                xSheet = InputBox("Digite el nombre de la hoja que desea importar", "Complete")
                conn = New OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;" & "data source=" & ExcelFile & "; " & "Extended Properties='Excel 12.0 Xml;HDR=Yes'")

                Try

                    Using Con As New SqlConnection(configuracion.ConnectionString)
                        conn.Open()
                        Con.Open()
                        'Crear tabla temporal con todas las columnas que se desea copiar
                        Dim consultaSQL As String = "CREATE TABLE #TempTable (CODIGO int not null, PROVINCIA varchar(45) not null, CANTON varchar(45) not null, [DISTRITO ELECTORAL] varchar(45) not null, CEDULA int not null, [1ER APELLIDO] varchar(45) not null, [2DO APELLIDO] varchar(45) not null, NOMBRE varchar(45) not null, SEXO int not null , FEC_NAC int not null)"
                        Dim cmd = New SqlCommand(consultaSQL, Con)
                        cmd.ExecuteNonQuery()

                        'Insertar datos en tabla temporal
                        da = New OleDbDataAdapter("SELECT * FROM [" & xSheet & "$]", conn)
                        ds = New DataSet
                        da.Fill(ds)
                        Using bulkCopy = New SqlBulkCopy(Con)
                            bulkCopy.DestinationTableName = "#TempTable"
                            bulkCopy.WriteToServer(ds.Tables(0)) 'dt contiene los datos que se desea importar
                        End Using

                        'Transferir de la tabla temporal a la tabla "destino" las filas no existentes 
                        cmd.CommandText = "INSERT INTO dbo.DATOS (CODIGO, PROVINCIA, CANTON, [DISTRITO ELECTORAL], CEDULA, [1ER APELLIDO], [2DO APELLIDO], NOMBRE, SEXO, FEC_NAC) 
    SELECT t.CODIGO, t.PROVINCIA, t.CANTON, t.[DISTRITO ELECTORAL], t.CEDULA, t.[1ER APELLIDO], t.[2DO APELLIDO], t.NOMBRE, t.SEXO, t.FEC_NAC FROM #TempTable t 
    WHERE NOT EXISTS (SELECT 1 FROM dbo.DATOS WHERE t.CEDULA = CEDULA); 
    DROP TABLE #TempTable;"

                        cmd.ExecuteNonQuery()

                    End Using



                    'MsgBox("Error" + ex.ToString, MsgBoxStyle.Information, "Informacion")
                Finally
                    conn.Close()


                    MessageBox.Show("DATOS REGISTRADOS")
                End Try
            End If
        End Sub


    • Editado Ruben_Mendoza martes, 21 de noviembre de 2017 17:54
    • Marcado como respuesta Ruben_Mendoza martes, 21 de noviembre de 2017 17:54
    • Desmarcado como respuesta Enrique M. Montejo miércoles, 22 de noviembre de 2017 7:11
    martes, 21 de noviembre de 2017 15:02
  • Me tira un error que dice que no se puede tener acceso a la tabla de destino "#TempTable" ------->
    martes, 21 de noviembre de 2017 16:41
  • Me tira un error que dice que no se puede tener acceso a la tabla de destino "#TempTable" ------->

    Fíjate que te advertí (con insistencia) que había que hacer todas las operaciones con LA MISMA conexión, porque si no la tabla no es accesible (se pierde si cierras la cnexión y no es visible si abres otra nueva).

    Aquí estás usando una conexión "Conn" para crear la tabla, y luego abres una conexión nueva al hacer el SqlBulkInsert (le pasas Conn.ConnectionString, con lo que saca de la conexión existente la cadena de conexión, y con esa cadena construye otra conexión nueva).

    Usa SqlBulkCopy(Conn) en lugar de SqlBulkCopy(Conn.ConnectionString), para que use la misma conexión (y no una nueva conexión basada en la misma cadena de conexión).

    martes, 21 de noviembre de 2017 17:07
  • Muchas gracias por su ayuda y paciencia, al fin me sirvio, gracias por todo, saludes
    martes, 21 de noviembre de 2017 17:55