Passa la parola chiave DEFAULT alla colonna non annullabile con SqlBulkCopy

c# sqlbulkcopy sql-server

Domanda

C'è un modo per passare la parola chiave DEFAULT a SqlBulkCopy da un IDataReader?
Fondamentalmente l'equivalente di

INSERT INTO MyTable(NonNullableWithDefault)
VALUES (DEFAULT)

Nella mia applicazione le tabelle di destinazione hanno colonne non annullabili con un vincolo predefinito su di esse.
Alcuni dei dati da caricare hanno valori per queste colonne, altri no.
Per le righe in cui non è presente alcun valore (DBNull), è necessario utilizzare il vincolo predefinito.

Come indicato in questa risposta, SqlBulkCopy si aspetta che tu non passi affatto in ColumnMapping o invii un valore.

Alternative che sto considerando

  • Dividere i dati per passare in colonne diverse (complesso)
  • Controlla DB per i valori predefiniti e riempiendoli nel datareader (lento)
  • Crea una tabella di dump separata senza vincoli in qualche modo (complesso, forse lento)

Non posso essere la prima persona con questo problema.
Cosa usano le persone in uno scenario del genere?

AGGIORNARE

Dopo un po 'di manipolazioni, ora recupererò l'intera struttura del database con una query e poi passerò il set di dati ai miei datareader.

SELECT
    columns.TABLE_SCHEMA                            schemaName
  , columns.TABLE_NAME                              tableName
  , columns.TABLE_SCHEMA + '.' + columns.TABLE_NAME fullTableName
  , columns.COLUMN_NAME                             columnName
  , columns.DATA_TYPE                               dataType
  , ISNULL(columns.CHARACTER_MAXIMUM_LENGTH, -1)    charlength
  , columns.COLUMN_DEFAULT                          defaultValue
  , columns.ORDINAL_POSITION                        ordinalPosition
FROM
  information_schema.columns columns

L'analisi della stringa del valore predefinito non è carina e molto probabilmente non è adatta ai casi avanzati. Nel nostro caso, tuttavia, copre tutte le basi.

private object ParseDefault(DataRow row)
{
    if(row.IsNull("defaultValue")) return null;

    var value = row.Field<string>("defaultValue");
    if (value == "(getdate())")
    {
        return DateTime.UtcNow;
    }
    var type = GetTypeForName(row.Field<string>("dataType"));
    return ParseCell(value.Trim('(', ')'), type);
}

private static object ParseCell(string value, Type info)
{
    if (value.Equals("NULL", StringComparison.OrdinalIgnoreCase))
    {
        return DBNull.Value;
    }

    int intValue;
    if (info == typeof(bool) && int.TryParse(value, out intValue))
    {
        return intValue == 1;
    }
    var foo = TypeDescriptor.GetConverter(info);
    return foo.ConvertFromInvariantString(value);
}

vantaggi:

  • Soluzione temporanea per valori predefiniti non annullabili
  • Cancella i messaggi di convalida (come la lunghezza massima)
  • Gestire la corrispondenza del nome del nome della colonna

svantaggi

  • analizzando la stringa del valore predefinito
  • non può risolvere i valori di default del lato sql (come sprocs)

Risposta accettata

C'è un modo per passare la parola chiave DEFAULT a SqlBulkCopy da un IDataReader?

Sfortunatamente no. :-( Se una colonna NOT NULL è NOT NULL e l'inserto di massa si associa ad essa, ogni riga deve avere un valore per quella colonna.

Background: A livello TDS, un bulk insert viene eseguito inviando un'istruzione SQL INSERT scritta utilizzando solo gli strumenti esterni sintassi seguite dalle strutture TDS contenenti i metadati della colonna e quindi i dati delle righe. Nessuno di questi fornisce un modo per dire "se un valore NULL trova in una colonna non NULL , considera NULL come DEFAULT ."

Cosa usano le persone in uno scenario del genere?

Hum ... è frustrante quando ci si sente come se ci fosse una soluzione integrata e semplice e invece tutte le opzioni sono dolorose. : - / Senza saperne di più sulla tua situazione, è difficile sapere quale opzione consigliare.

Un'ulteriore opzione: l'approccio al valore dei segnaposto. Prima di inserire l'inserto, sostituire " NULL s che deve essere impostato come predefinito" con un valore di segnaposto. Quindi, post-insert, eseguire un aggiornamento (s) per sostituire il segnaposto (s) con valori predefiniti generati dal database (ad esempio UPDATE ... SET Col1 = DEFAULT WHERE Col1 = *placeholder* ). L'applicazione dovrebbe sapere quali colonne non annullabili hanno valori predefiniti ma non dovrebbero sapere come calcolare i valori predefiniti. Non mi interessa perché usa i cosiddetti numeri magici - ma è un'opzione.



Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché