iwan
Goto Top

MS SQL 2005er Express - Update einer Tabelle aus 2. DB

Guten morgen zusammen,

ich habe 2 Datenbanken, die beide die gleiche Tabelle "Datentab" (13 Spalten) enthalten.
Die Tabelle in DB1 enthält diverse Daten, die Tabelle in DB2 ist leer.
Nun möchte ich die Tabelle in DB2 mit Daten aus DB1 füllen.
Allerdings soll hier ein Kriterium (ArtNr=4712) berücksichtigt werden.

Die Selektion der Daten in DB1 ist kein Problem:
SECLECT * FROM Datentab
WHERE ArtNr=4712
aber wie kriege ich das Ergebnis nun in DB2 rein?

Bei einer geringen Zeilenanzahl habe ich das ganze bisher mit Copy&Paste gemacht.
Das ist allerdings bei den heutigen Datenmenge kaum noch machbar oder dauert ewig.

Hat hier jemand einen Tip für mich?

Ja, ich habe Google schon befragt, aber komme da nicht weiter.
Nein, ich möchte keine zusätzlichen Programme oder Tools verwenden.
Tablediff.exe habe ich schon probiert, aber ist mir zu "unhandlich".


*nachtrag*
Ich habe doch noch was rausgefunden (beide DB_Server sind verbunden):
INSERT INTO DB2.Daten.dbo.Datentab SELECT  ArtNr, Beschr, Preis FROM DB1.Daten.dbo.Datentab WHERE ArtNr=4712
Das schmeisst mir aber einen Fehler raus:
Meldung 7202, Ebene 11, Status 2, Zeile 1
Der Server 'DB1' wurde in sys.servers nicht gefunden. Prüfen Sie, ob der richtige Servername angegeben wurde. Führen Sie bei Bedarf die gespeicherte Prozedur sp_addlinkedserver aus, um den Server zu sys.servers hinzuzufügen.  

Content-Key: 176924

Url: https://administrator.de/contentid/176924

Ausgedruckt am: 28.03.2024 um 23:03 Uhr

Mitglied: Indrador
Indrador 28.11.2011 um 12:27:10 Uhr
Goto Top
Guten Tag,

also deine Datenbanken liegen auf 2 verschiedenen Servern, hast du denn auf dem Server, auf dem die Abfrage läuft auch den anderen Server als Linkserver eingerichtet?
Lauf Fehlermeldung scheinbar nicht, also erstmal Linkserver anlegen:

EXEC master.dbo.sp_addlinkedserver @server = N'DB1', @srvproduct=N'SQL Server'  
GO
EXEC master.dbo.sp_addlinkedsrvlogin @rmtsrvname=N'DB1',@useself=N'False',@locallogin=NULL,@rmtuser=N'USERNAME',@rmtpassword='PASSWORT'  
GO
EXEC master.dbo.sp_serveroption @server=N'DB1', @optname=N'collation compatible', @optvalue=N'false'  
GO
EXEC master.dbo.sp_serveroption @server=N'DB1', @optname=N'data access', @optvalue=N'true'  
GO
EXEC master.dbo.sp_serveroption @server=N'DB1', @optname=N'dist', @optvalue=N'false'  
GO
EXEC master.dbo.sp_serveroption @server=N'DB1', @optname=N'pub', @optvalue=N'false'  
GO
EXEC master.dbo.sp_serveroption @server=N'DB1', @optname=N'rpc', @optvalue=N'false'  
GO
EXEC master.dbo.sp_serveroption @server=N'DB1', @optname=N'rpc out', @optvalue=N'false'  
GO
EXEC master.dbo.sp_serveroption @server=N'DB1', @optname=N'sub', @optvalue=N'false'  
GO
EXEC master.dbo.sp_serveroption @server=N'DB1', @optname=N'connect timeout', @optvalue=N'0'  
GO
EXEC master.dbo.sp_serveroption @server=N'DB1', @optname=N'collation name', @optvalue=null  
GO
EXEC master.dbo.sp_serveroption @server=N'DB1', @optname=N'lazy schema validation', @optvalue=N'false'  
GO
EXEC master.dbo.sp_serveroption @server=N'DB1', @optname=N'query timeout', @optvalue=N'0'  
GO
EXEC master.dbo.sp_serveroption @server=N'DB1', @optname=N'use remote collation', @optvalue=N'true'  
GO

Du musst @135404=N'DB1', überall mit dem richtigen Hostnamen ersetzen UND
@rmtuser=N'USERNAME',@rmtpassword='PASSWORT' mit dem richtigen Usernamen und Passwort.


Wenn das der Fall ist und der Server angelegt ist, geht der Insert so:
-- Abfrage auf DB2 ausführen
-- Das InsertStatement zeigt auf DB2
INSERT INTO Daten.dbo.Datentab (Feld1, Feld2)
-- Der Openquery Select zeigt auf DB1
SELECT * from OPENQUERY (LINKSERVERNAME, 'select Feld1, Feld2 from Daten.dbo.Datentab WHERE ArtNr=4712 with (nolock)')  
Mitglied: Iwan
Iwan 28.11.2011 um 13:11:15 Uhr
Goto Top
Vielen dank, das mit dem registrieren hatte ich schon versucht und er tauchte auch unter 'Registrierte Server' auf.
Allerdings brachte die Abfrage
SELECT * FROM sys.servers
immer nur den lokalen Server.
Dann habe ich es per 'sp_addlinkedserver' versucht, aber wohl nicht die richtigen Befehle oder Werte genommen.
Nun habe ich es so gemacht, wie du beschrieben hast, und nun geht auch der Datentransfer.

Nochmals vielen dank!


Gäbe es damit eigentlich auch die Möglichkeit, nur fehlende Einträge zu ergänzen oder sogar vorhandene zu aktualisieren?
Beim Insert werden nämlich Pirmary Keys verletzt.
Mitglied: Indrador
Indrador 28.11.2011 um 17:08:02 Uhr
Goto Top
Hi, mir fallen spontan zwei Möglichkeiten ein:

MÖGLICHKEIT 1:

Die sp_addlinkedserver auf dem DB1 ausführen mit Daten von DB2, nun kennst du DB2 auf DB1 und auf DB1 kennst du DB2.

hier machst du nun folgedes auf dem Server, der die fehlenden Daten hat (DB1):
Create View v_Differenz_DB1_zu_DB2
AS
Select Feld1, Feld2, .... from Daten.dbo.Datentab where PK not in (
SELECT * from OPENQUERY (DB2, 'select PK from Daten.dbo.Datentab with (nolock)')  
Go
nun hast du auf DB1 eine View, in der nur Datensätze sind, die es auf DB2 nicht gibt.


Dann ersetzt du in dem Insert Schnipselchen von Oben
SELECT * from OPENQUERY (LINKSERVERNAME, 'select Feld1, Feld2 from Daten.dbo.Datentab WHERE ArtNr=4712 with (nolock)')  
mit
SELECT * from OPENQUERY (LINKSERVERNAME, 'select Feld1, Feld2 from Daten.dbo.v_Differenz_DB1_zu_DB2')  

MÖGLICHKEIT 2:

auf DB2 folgendes:

Create View v_Datenbestand_DB1
AS
SELECT * from OPENQUERY (DB1, 'select * from Daten.dbo.Datentab with (nolock)'  
GO
INSERT INTO Daten.dbo.Datentab 
SELECT  ArtNr, Beschr, Preis FROM Daten.dbo.v_Datenbestand_DB1 WHERE Artnr not in (Select Artnr from Daten.dbo.Datentab (nolock))

Ich kann grade nichts testen, bin unterwegs, falls irgendwelche Fehler geworfen werden, meld dich einfach nochmal mit kompletter Meldung.
Gruß
Mitglied: Iwan
Iwan 29.11.2011 um 09:54:47 Uhr
Goto Top
Guten morgen,

ich bevorzuge Möglichkeit 2, da ich dies dann auf jedem x-beliebigem Rechner machen kann.
Dazu muss ich dann nur auf dem Zielrechner die Quelle registrieren.

Nur noch mal zur Sicherheit:
DB1 = Quelle, DB2 = Ziel

Ich habe das mit dem "Create View" auf DB2 in einem SQL-Query ausprobiert.
Erst erhielt ich eine Fehlermeldung, aber da fehlt am Ende von Zeile 3 eine Klammer.

1. bei einem zweiten Aufruf erhalte ich die Meldung, das 'v_Datenbestand_DB1' schon existiert
Wie kann ich die zunächst löschen (per Skript, nicht manuell), damit sie mit jedem Aufruf neu erstellt wird?

2. wie kann ich den Filter auf ArtNr=4711 einbauen?
Ich habe Zeile 3 entsprechend erweitert:
SELECT * from OPENQUERY (DB1, 'select * from Daten.dbo.Datentab with (nolock) where ArtNr=4711'   
In der v_Datenbestand_DB1 stehen dann auch nur die selektierten, aber es erfolgt dann Abgleich mehr mit der Zieltabelle.
Zumindest schreibt er die fehlenden Einträge nicht rein.
Ich vermute mal, das es an der schon vorhandenen ArtNr liegt, denn wenn ich als Kriterium im 2. Teil 2. Zeile statt ArtNr mal Preis ausgewählt habe, dann funktioniert es.
Ist aber kein Problem, wenn man es weiß, nur so bekomme ich halt kein Update auf vorhandene Sätze.
Dann lösch ich halt vorhandene vorher und gut ist's.
Mitglied: Indrador
Indrador 29.11.2011 um 10:48:33 Uhr
Goto Top
Zitat von @Iwan:
Nur noch mal zur Sicherheit:
DB1 = Quelle, DB2 = Ziel

Ja so soll es sein, DB1 ist die Quelle des Vergleichs, weil da gibt es mehr Daten.

Ich habe das mit dem "Create View" auf DB2 in einem SQL-Query ausprobiert.
Erst erhielt ich eine Fehlermeldung, aber da fehlt am Ende von Zeile 3 eine Klammer.

Welch eine Nachlässigkeit von mir, war aber wie gesagt unterwegs =)

1. bei einem zweiten Aufruf erhalte ich die Meldung, das 'v_Datenbestand_DB1' schon existiert
Wie kann ich die zunächst löschen (per Skript, nicht manuell), damit sie mit jedem Aufruf neu erstellt wird?

IF  EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[v_Datenbestand_DB1]'))  
DROP VIEW [dbo].[v_Datenbestand_DB1]
GO
Über das Create view, allerdings weiß ich nicht, warum du die view jedes mal neu erstellen willst/musst, sie liefert immer ein dynamisches Ergebnis.
Stell dir das so vor, du führst

Select Feld1, Feld2, .... from Daten.dbo.Datentab where PK not in ( SELECT * from OPENQUERY (DB2, 'select PK from Daten.dbo.Datentab with (nolock)')  
aus dem Management Studio mit F5 aus und erhälst 2 Datensätze. Diese zwei fügst du nun in deine zweite Tabelle ein und drückst wieder F5 nun erhälst du nichts mehr.
In der View passiert das gleiche, wenn du Sie aufrust, kriegst du 2 Datensätze, nach dem Insert 0.
Eine View ist praktisch sowas wie eine gewisse Query statisch anlegen und in der View ist immer das Ergebnis der Query zur Laufzeit.

Wenn das nicht verständlich ist, meld dich noch mal dazu.


2. wie kann ich den Filter auf ArtNr=4711 einbauen?
Ich habe Zeile 3 entsprechend erweitert:
SELECT * from OPENQUERY (DB1, 'select * from Daten.dbo.Datentab with (nolock) where ArtNr=4711'  
> 
In der v_Datenbestand_DB1 stehen dann auch nur die selektierten, aber es erfolgt dann Abgleich mehr mit der Zieltabelle.
Zumindest schreibt er die fehlenden Einträge nicht rein.
Ich vermute mal, das es an der schon vorhandenen ArtNr liegt, denn wenn ich als Kriterium im 2. Teil 2. Zeile statt ArtNr mal
Preis ausgewählt habe, dann funktioniert es.
Ist aber kein Problem, wenn man es weiß, nur so bekomme ich halt kein Update auf vorhandene Sätze.
Dann lösch ich halt vorhandene vorher und gut ist's.

Hier kann ich dir ehrlich gesagt jetzt grade nur helfen, wenn du mir einmal den Aufbau von DB1..Tabelle und DB2..Tabelle
beschreibst und auf welchen Spalten die einzufügenden Differenzen sind. Die Abfrage dann zu finden ist kein Problem.
Mitglied: Iwan
Iwan 29.11.2011 um 12:36:32 Uhr
Goto Top
Ich hatte es mit DELETE probiert, weil ich DROP noch nicht kannte, aber man lernt nie aus face-wink

Das mit dem Löschen bevor ich die Daten neu einlese, ist kein Problem.
Daher ist mir das Aktualisieren der Daten jetzt nicht so wichtig.
Die beiden Tabellen sind wie folgt aufgebaut:
ArtNr (4-stellige Zahl), KdKreis (2-stellige Zahl), Beschr (Text), Preis (Zahl)
Vergleichswerte wäre in dem Fall z.Bsp. der KdKreis, wo dann die Beschr und der Preis aktualisiert werden müssten.
Der Artikel wäre dann ja schon vordefiniert bzw. gefiltert (4711).
Mitglied: Indrador
Indrador 29.11.2011 um 14:55:51 Uhr
Goto Top
Zitat von @Iwan:
Das mit dem Löschen bevor ich die Daten neu einlese, ist kein Problem.
Daher ist mir das Aktualisieren der Daten jetzt nicht so wichtig.

Jetzt müssen wir noch einmal begriffe abgrenzen:
Reden vir von anfügen oder aktualisieren?

Anfügen wäre in Tabelle2 gibt es Datensatz X nicht, aktualisieren wäre in Tabelle2 Gibt es
Datensatz X und im Feld muss nun der Wert Y statt Z stehen.

Beispiel für Anfügen (Datensatz von Kunde 113 fehlt noch komplett in Tabelle 2 und muss da rein):
Tabelle DB1
Artikel		Kunde		Preis
4711		111			5,00
4711		112			7,00
4711		113			8,00
Tabelle DB2
Artikel		Kunde		Preis
4712		111			5,00
4713		112			7,00

Beispiel für aktualisieren (Kunde 113 der Preis muss in Tabelle 2 auch auf 1,00):
Tabelle DB1
Artikel		Kunde		Preis
4711		111			5,00
4711		112			7,00
4711		113			1,00
Tabelle DB2
Artikel		Kunde		Preis
4711		111			5,00
4711		112			7,00
4711		113			8,00

Gruß
Mitglied: Iwan
Iwan 29.11.2011 um 15:33:08 Uhr
Goto Top
Sowohl als auch:
- es kann sein, das ein neuer Datensatz in DB1 steht, aber nicht in DB2
- es kann sein, das ein veränderter Datensatz in DB1 steht, aber nicht in DB2
Selten kann es vorkommen, das ein Datensatz in DB1 gelöscht wurde, aber in DB2 noch vorhanden ist

Mir geht es eigentlich nur um eine (einfache) Möglichkeit, 2 Tabellen auf 2 Servern bei Bedarf quasi zu synchronisieren.
Da geht es mittlerweile um mehrere tausend Datensätze und mit Copy&Paste ist das einfach nicht mehr wirklich machbar.
Deswegen sagte ich ja, das das Löschen und Neueinlesen wohl der einfachste Weg wäre.
Mitglied: Indrador
Indrador 29.11.2011 um 16:01:00 Uhr
Goto Top
Wenn der seltene Fall, dass in DB1 was gelöscht wird
eintritt, soll der Datensatz auch in DB2 weg sein oder?

Wenn das der Fall ist, mach doch einfach folgendes:

Auf DB1 eine View mit dem Tabelleninhalt, die brauchst du nur einmal anlegen und dann nicht mehr ändern.
Create View v_Befuellung_DB2
AS
Select * from Daten.dbo.Datentab (nolock)
GO

Auf Server 2:
Delete from Daten.dbo.Datentab
GO
INSERT INTO Daten.dbo.Datentab  
SELECT  * FROM Daten.dbo.v_Befuellung_DB2
Die Codepassage für Server 2 packste dir dann in einen Stores Procedure und die rufst du auf DB 2 einfach stündlich oder täglich Abends per SQLcmd auf,
dann hast du Synchronisierung ohne zutun.
Der bessere Weg wäre das Script direkt als SQL Job laufen zu lassen aber ich meine der Express Server hat keinen SQL Server Agent.

In mittleren bis großen Umgebungen (>1.000.000 Datensätze) würde man sich tatsächlich die Mühe für ein bidirektionales Differenzskript machen
aber bei "mehreren Tausend" Datensätzen, ist die Menge so schwindend gering für eine Datenbank, dass alles löschen und neu reinschreiben
vollkommen okay ist.
Mitglied: Iwan
Iwan 30.11.2011 um 09:56:32 Uhr
Goto Top
Mit den Skripten komme ich auf jeden Fall schon mal weiter und spare mir nervenaufreibendes manuelles Kopieren von Daten.
Die Tabelle 'Datentab' war jetzt eigentlich nur ein Beispiel - das ganze werde ich entsprechend auch für andere Tabellen nutzen.

Auf jeden Fall vielen dank noch mal für die Hilfestellung.
Mitglied: Indrador
Indrador 30.11.2011 um 11:34:14 Uhr
Goto Top
Kein Problem, dann mal viel Erfolg.