Laden...

mySQL INSERT INTO => Parameter aus Tabelle

Erstellt von dahaack vor 12 Jahren Letzter Beitrag vor 12 Jahren 7.235 Views
D
dahaack Themenstarter:in
47 Beiträge seit 2011
vor 12 Jahren
mySQL INSERT INTO => Parameter aus Tabelle

Hallo,

ich möchte in mySQL bei einem INSERT Befehl einen Primary Key aus einer Tabelle erhalten. Leider funktioniert das nicht so, wie ich es mir vorstelle.

Bei der INSERT ... SELECT Befehlkombination können soweit ich weiß nur alle Parameter vollständig aus Tabellen gesetzt werden. Ich möchte aber nur einen Parameter aus einer Tabelle haben, und die anderen 'selbst' übergeben.

Mit folgendem Code funktionierts leider nicht, obwohl die SELECT Anweisung den richtigen Rückgabewert hat:

INSERT INTO tableUserName (userId, name, startdate)
VALUES ((	SELECT tableUser.id
			FROM tableUser, tableUserName
			WHERE tableUser.id=userId AND name='Benutzer 2'),
		'Neuer Benutzername',
		Now());

Fehlermeldung:
You can't specify target table 'tableUserName' for update in FROM clause

Kann mir jemand helfen? Vielen Dank schonmal!

Grüße
dahaack

N
20 Beiträge seit 2010
vor 12 Jahren

Das Statement macht für mich überhaupt keinen Sinn. Kann es sein, dass du eigtl einen Datensatz aktualisieren willst, anstatt einen neuen in die Tabelle einzufügen?

Wenn ja, schau dir mal das UPDATE-Statement an.

D
dahaack Themenstarter:in
47 Beiträge seit 2011
vor 12 Jahren

Hallo Nuky,

doch das Statement macht Sinn. Ein Benutzer kann seinen Benutzernamen ändern, alle alten Benutzername und der Zeitpunkt der Änderungen bleibt aber in der Datenbank gespeichert.

N
20 Beiträge seit 2010
vor 12 Jahren

Folgendes Statement nennt alle Benutzer, deren Name "Benutzer 2" ist, um, setzt das Feld "startdate" auf das aktuelle Datum und lässt alle anderen Felder unangetastet:

UPDATE [tableUserName] SET name="Neuer Name", startdate=Now() WHERE name="Benutzer 2"
D
dahaack Themenstarter:in
47 Beiträge seit 2011
vor 12 Jahren

Hallo Nuky,

nein, das ist leider nicht das, was ich suche. Nun wird ja der bisherige Benutzername überschrieben, und genau das soll nicht passieren. Ich möchte tatsächlich den INSERT Befehl benutzen und der Parameter userId soll den Wert bekommen, der bei der SELECT Anweisung als einziger zurückgegeben wird.

Die beiden Tabellen sehen so aus:

CREATE TABLE tableUser (
	id INT NOT NULL AUTO_INCREMENT,
	password VARCHAR(24),
	PRIMARY KEY(id)
);


CREATE TABLE tableUserName (
	id INT NOT NULL AUTO_INCREMENT,
	userId INT NOT NULL,
	name CHAR(48),
	startdate DATETIME,
	PRIMARY KEY(id),
	FOREIGN KEY (userId) REFERENCES tableUser(id)
);
N
20 Beiträge seit 2010
vor 12 Jahren

Ok, dann sollte folgendes funktionieren:


INSERT INTO tableUserName (userId, name, startdate)
VALUES 
( (SELECT tableUser.id
      FROM tableUser 
        INNER JOIN tableUserName ON tableUserName.userId = tableUser.id
	   WHERE tableUserName.name='Benutzer 2'),
    'Neuer Benutzername',
     Now());

D
dahaack Themenstarter:in
47 Beiträge seit 2011
vor 12 Jahren

Hallo Nuky,

leider klappt es nicht, die Fehlermeldung ist leider die gleiche.

N
20 Beiträge seit 2010
vor 12 Jahren

Und wenn du es einfach "auseinanderziehst"?


DECLARE @userid int

SELECT @userid = ( 
  SELECT tableUser.id
      FROM tableUser 
        INNER JOIN tableUserName ON tableUserName.userId = tableUser.id
	   WHERE tableUserName.name='Benutzer 2'))

INSERT INTO tableUserName (userId, name, startdate)
VALUES (@userid, 'Neuer Benutzername', Now());

Achtung: Ob das MySQL-kompatible Syntax ist weiß ich nicht.

D
dahaack Themenstarter:in
47 Beiträge seit 2011
vor 12 Jahren

Hallo Nuky,

danke für deine Antwort(en)!!!

Dein Ansatz wird sicherlich funktionieren. Leider krieg ich es selbst nach mehreren Stunden nichtmal hin eine Variable zu deklarieren. Egal ob mit, oder ohne Trigger, mit DELIMITER oder ohne, egal von welcher Seite ich einen Codeschnipsel ausprobiere. 🙁

Ich hoffe, dass mir dabei nochmal jemand helfen kann. Kann mir jemand den folgenden Code berichtigen, sodass (mit oder ohne Trigger) eine Variable deklariert wird, die ich später in einem INSERT Befehl benutzen kann?

DELIMITER |
CREATE TRIGGER trigger_name BEFORE INSERT
ON tableUserName FOR EACH ROW
BEGIN
DECLARE @test = 1; 
END;
|
DELIMITER;

Fehlermeldung:
Fatal error encountered during command execution.

Danke.

N
20 Beiträge seit 2010
vor 12 Jahren

Das sagt die MySQL-Doku dazu:

Deklaration lokaler Variablen in MySQL

Setzen geht wohl mit "SET".

Ergo sollte folgendes funktionieren:


START TRANSACTION;

DECLARE userid INT

SET @userid = ( 
  SELECT tableUser.id
      FROM tableUser 
        INNER JOIN tableUserName ON tableUserName.userId = tableUser.id
	   WHERE tableUserName.name='Benutzer 2'))

INSERT INTO tableUserName (userId, name, startdate)
VALUES (userid, 'Neuer Benutzername', Now());

COMMIT;

Das herauszufinden hat mich keine 5min gekostet. Das hättest du auch geschafft 😉

656 Beiträge seit 2008
vor 12 Jahren

Nur mal so nebenbei, wenn du ja so oder so eine Art Login hast, wo du dir Benutzerdaten merkst...sollte dir die UserId nicht schon bekannt sein, wenn du das Insert durchführen willst?

1.820 Beiträge seit 2005
vor 12 Jahren

Hallo!

Nur nochmal zur Erklärung der Fehlerursache:
Im INSERT-Statement wird dieselbe Tabelle verwendet, wie im INNER JOIN des SELECT-Statements. Dadurch entsteht eine zyklische Abhängigkeit zwischen den beinen Einzelstatements, welche zum Abbruch führen. Vermutlich tritt der Fehler beim Versuch auf, die Anfrage intern zu optimieren und/oder die zu verarbeitenden Werte voraussagen zu können.

Nobody is perfect. I'm sad, i'm not nobody 🙁

D
dahaack Themenstarter:in
47 Beiträge seit 2011
vor 12 Jahren

Hallo an alle!

Das sagt die MySQL-Doku dazu:


>


>

Ergo sollte folgendes funktionieren:

  
START TRANSACTION;  
  
DECLARE userid INT  
  
SET @userid = (   
  SELECT tableUser.id  
      FROM tableUser   
        INNER JOIN tableUserName ON tableUserName.userId = tableUser.id  
     WHERE tableUserName.name='Benutzer 2'))  
  
INSERT INTO tableUserName (userId, name, startdate)  
VALUES (userid, 'Neuer Benutzername', Now());  
  
COMMIT;  
  

Das herauszufinden hat mich keine 5min gekostet. Das hättest du auch geschafft 😉

Erstmal danke, dass du das für mich gegooglelt hast. 😉

Aber der Code verursacht einen Absturz.

Fehlermeldung:
Fatal error encountered during command execution.

Auch wenn ich ihn modifiziere, zum Beispiel den "SET @userid =" Befehl rausnehme kommt eine Fehlermeldung wegen der Syntax.

Entweder ist die Doku von mySQL grottenschlecht, oder ich / wir haben nicht verstanden wie sie anzuwenden ist.

Nur mal so nebenbei, wenn du ja so oder so eine Art Login hast, wo du dir Benutzerdaten merkst...sollte dir die UserId nicht schon bekannt sein, wenn du das Insert durchführen willst?

Da hast du vermutlich recht. Ich mache derzeit einen Crashkurs und spiel einfach mit ein paar Befehlen rum um zu gucken was funktioniert und was nicht. Wenn ich mich nicht irre, würde diese Befehlsfolge bei einer ORACLE Datenbank funktionieren.

Nur nochmal zur Erklärung der Fehlerursache:
Im INSERT-Statement wird dieselbe Tabelle verwendet, wie im INNER JOIN des SELECT-Statements. Dadurch entsteht eine zyklische Abhängigkeit zwischen den beinen Einzelstatements, welche zum Abbruch führen. Vermutlich tritt der Fehler beim Versuch auf, die Anfrage intern zu optimieren und/oder die zu verarbeitenden Werte voraussagen zu können.

Danke für den Hinweis, Tom!

@ all:

Ich habe mir heute 2 Bücher über MySQL ausgeliehen. Damit werd ich mir bestimmt einen besseren Überblick über die MySQL Datenbank verschaffen.

Vielen Dank an alle und noch nen schönen Abend!!!

C
1.214 Beiträge seit 2006
vor 12 Jahren

Fehlermeldung:
Fatal error encountered during command execution.

Kommt das über den .NET Connector? Hört sich für mich nämlich so an. Ich kann die Syntax von MySql jetzt nicht auswendig und hab kein MySql zur Hand, aber das findest du natürlich am besten raus, indem du zuerst direkt in der MySql Konsole ausprobierst, was du machen willst, bevor du das über dein C# Programm abschickst. Da solltest du dann hoffentlich sinnvollere Fehlermeldungen bekommen.

656 Beiträge seit 2008
vor 12 Jahren

Ich mache derzeit einen Crashkurs und spiel einfach mit ein paar Befehlen rum um zu gucken was funktioniert und was nicht. Wenn ich mich nicht irre, würde diese Befehlsfolge bei einer ORACLE Datenbank funktionieren.

Eventuell funktioniert auch

insert into Table(col1, col2, col3)
select otherColumn1, 'value2', 3 from OtherTable where otherId = 42

...zumindest lässt MSSQL sowas zu (anstatt der Projektion als ein Wert im Insert benutzt du das komplette Ergebnis eines Select als Zeile; mit Spalten und fixen Werten gemischt)

D
dahaack Themenstarter:in
47 Beiträge seit 2011
vor 12 Jahren

Kommt das über den .NET Connector? Hört sich für mich nämlich so an. Ich kann die Syntax von MySql jetzt nicht auswendig und hab kein MySql zur Hand, aber das findest du natürlich am besten raus, indem du zuerst direkt in der MySql Konsole ausprobierst, was du machen willst, bevor du das über dein C# Programm abschickst. Da solltest du dann hoffentlich sinnvollere Fehlermeldungen bekommen.

Hmm, es gibt zwar eine Eingabeaufforderung... aber ohne einen funktionierenden Strg+V Befehl ist sowas heute denke ich nicht mehr zumutbar. Ein Programm auf Windows Ebene mit einer genaueren Fehlerbeschreibung habe ich leider nicht gefunden.

Zuzeit habe ich eine C# Website, die den Code auf Knopfdruck aus einer Textdatei ausliest und eine Exception oder eine Erfolgreich-Meldung sowie ein paar Tabellen ausgibt.

Ich mache derzeit einen Crashkurs und spiel einfach mit ein paar Befehlen rum um zu gucken was funktioniert und was nicht. Wenn ich mich nicht irre, würde diese Befehlsfolge bei einer ORACLE Datenbank funktionieren.

Eventuell funktioniert auch

insert into Table(col1, col2, col3)  
select otherColumn1, 'value2', 3 from OtherTable where otherId = 42  

...zumindest lässt MSSQL sowas zu (anstatt der Projektion als ein Wert im Insert benutzt du das komplette Ergebnis eines Select als Zeile; mit Spalten und fixen Werten gemischt)

Hallo BhaaL,

der Code funktioniert auch in MySql. Ich habe ihn nochmal etwas verändert und auf das aktuelle Problem angepasst und es klappt!

INSERT INTO tableUserName(userId, name, startDate)
SELECT tableUserName.id, 'Neuer Benutzer', Now()
FROM tableUserName
WHERE tableUserName.name = 'Benutzer 2'

Da hätt ich aber auch mal selbst drauf kommen können... 😉

Nochmals danke an Euch!

C
1.214 Beiträge seit 2006
vor 12 Jahren

Hmm, es gibt zwar eine Eingabeaufforderung... aber ohne einen funktionierenden Strg+V Befehl ist sowas heute denke ich nicht mehr zumutbar.

Die Windows Konsole ist zwar an sich schon eine Zumutung und da geht auch kein Strg+V, aber kopieren und einfügen kann man damit trotzdem. Und Guis für Mysql gibts auch viele, das sollte nicht das Problem sein.

D
dahaack Themenstarter:in
47 Beiträge seit 2011
vor 12 Jahren

Hallo Coder007,

ich habe mir jetzt den MySQL Workbench runtergeladen. Die erste Fehlermeldung, die ich gepostet habe ist gleich geblieben. Ich denke, dass Toms Vermutung über die Ursache des Fehlers richtig ist.

Die letzten Codes von Nuky haben folgende Fehlermeldungen:

DECLARE @userid int

SELECT @userid = ( 
  SELECT tableUser.id
      FROM tableUser 
        INNER JOIN tableUserName ON tableUserName.userId = tableUser.id
	   WHERE tableUserName.name='Benutzer 2'))

INSERT INTO tableUserName (userId, name, startdate)
VALUES (@userid, 'Neuer Benutzername', Now());

Fehlermeldung:
Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DECLARE' at line 1

START TRANSACTION;

DECLARE tempuserid INT

SET @tempuserid = ( 
  SELECT tableUser.id
      FROM tableUser 
        INNER JOIN tableUserName ON tableUserName.userId = tableUser.id
	   WHERE tableUserName.name='Benutzer 2'))

INSERT INTO tableUserName (userId, name, startdate)
VALUES (tempuserid, 'Neuer Benutzername', Now());

COMMIT;

Fehlermeldung:
Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DECLARE' at line 1

Auch Variationen mit DELIMITER und sonstigem habe ich ausprobiert, irgendwo meckert er immer. 😦

Da der oben genannte INSERT INTO ... SELECT Befehl erstmal funktioniert reicht mir das erstmal. Aber eine genauere Fehlerbeschreibung ist mit diesem Tool möglich. Ich beschäftige mich jetzt aber erstmal mit Funktionen und Prozeduren.

Nochmals danke an alle für die Hilfe.

C
1.214 Beiträge seit 2006
vor 12 Jahren

Fehlermeldung:
Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DECLARE' at line 1

Und was sagt die Doku?

DECLARE is permitted only inside a BEGIN ... END compound statement and must be at its start, before any other statements.

D
dahaack Themenstarter:in
47 Beiträge seit 2011
vor 12 Jahren

Ich weiß.

Und bei BEGIN... END steht wiederum:

Um überhaupt mehrere Anweisungen verbinden zu können, muss ein Client in der Lage sein, Strings von Anweisungen zu senden, die durch das Trennzeichen ; abgegrenzt sind. Dafür sorgt der Kommandozeilen-Client mysql mit dem Befehl delimiter.

Ein komplettes Beispiel scheint es in der Doku nicht zu geben. Sorry Coder, ich bin dir deiner Hilfe sehr dankbar, aber ich habe kein Bock mehr auf dieses Beispiel. Ich habe x viele verschiedene Versionen mit und ohne DELIMITER, mit und ohne BEGIN, mit und ohne Semikolon an entsprechenden stellen, mit und ohne transaktion an der oder der Stelle, immer meckert er.

delimiter $$
begin
START TRANSACTION;

DECLARE tempuserid INT

SET @tempuserid = ( 
  SELECT tableUser.id
      FROM tableUser 
        INNER JOIN tableUserName ON tableUserName.userId = tableUser.id
	   WHERE tableUserName.name='Benutzer 2')

INSERT INTO tableUserName (userId, name, startdate)
VALUES (tempuserid, 'Neuer Benutzername', Now());

COMMIT;
end$$
delimiter;

Fehlermeldung:
Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'START TRANSACTION;

DECLARE tempuserid INT

SET @tempuserid = (
SELECT tableU' at line 2

Mit Semikolon hinter der Deklaration:

delimiter $$
begin
START TRANSACTION;

DECLARE tempuserid INT;

SET @tempuserid = ( 
  SELECT tableUser.id
      FROM tableUser 
        INNER JOIN tableUserName ON tableUserName.userId = tableUser.id
	   WHERE tableUserName.name='Benutzer 2')

INSERT INTO tableUserName (userId, name, startdate)
VALUES (tempuserid, 'Neuer Benutzername', Now());

COMMIT;
end$$
delimiter;

Fehlermeldung:
Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'START TRANSACTION;

DECLARE tempuserid INT;

SET @tempuserid = (
SELECT table' at line 2

Ohne Transaktion:

delimiter $$
begin

DECLARE tempuserid INT;

SET @tempuserid = ( 
  SELECT tableUser.id
      FROM tableUser 
        INNER JOIN tableUserName ON tableUserName.userId = tableUser.id
	   WHERE tableUserName.name='Benutzer 2')

INSERT INTO tableUserName (userId, name, startdate)
VALUES (tempuserid, 'Neuer Benutzername', Now());

COMMIT;
end$$
delimiter;

Fehlermeldung:
Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DECLARE tempuserid INT;

SET @tempuserid = (
SELECT tableUser.id
FROM t' at line 3

Eine andere Variante des SELECT Befehls:

delimiter $$
begin

DECLARE tempuserid INT;

  
  SELECT tableUser.id
      FROM tableUser 
        INNER JOIN tableUserName ON tableUserName.userId = tableUser.id
	   WHERE tableUserName.name='Benutzer 2' into tempuserid

INSERT INTO tableUserName (userId, name, startdate)
VALUES (tempuserid, 'Neuer Benutzername', Now());

COMMIT;
end$$
delimiter;

Fehlermeldung:
Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DECLARE tempuserid INT;

SELECT tableUser.id
FROM tableUser
' at line 3

Mit START TRANSAKTION und Semikolon hinter der Deklaration:

delimiter $$
begin
START TRANSACTION;

DECLARE tempuserid INT;

SET @tempuserid = ( 
  SELECT tableUser.id
      FROM tableUser 
        INNER JOIN tableUserName ON tableUserName.userId = tableUser.id
	   WHERE tableUserName.name='Benutzer 2')

INSERT INTO tableUserName (userId, name, startdate)
VALUES (tempuserid, 'Neuer Benutzername', Now());

COMMIT;
end$$
delimiter;

Fehlermeldung:
Error Code: 1064. You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'START TRANSACTION;

DECLARE tempuserid INT;

SET @tempuserid = (
SELECT table' at line 2

... und etliche mehr habe ich ausprobiert. 😦

C
1.214 Beiträge seit 2006
vor 12 Jahren

Sorry Coder, ich bin dir deiner Hilfe sehr dankbar, aber ich habe kein Bock mehr auf dieses Beispiel.

Mir ist doch egal, ob du dieses Beispiel machst oder nicht 😉 Ich will dich nur dazu animieren, genauer zu suchen bzw. genauer die Dokus zu lesen. Ich hab wie gesagt kein MySql zur Hand und weiß es nicht auswendig, bin mir aber ziemlich sicher, dass du hier auf einem trivialen Problem hockst. Dein direktes Problem ist doch erstmal ein Syntax Fehler. Es sollte nicht so schwer sein, das in den Griff zu bekommen. DECLARE ist für lokale Variablen in stored procedure und ähnlichen gedacht. Hast du hier nicht. Wenn ich das richtig sehe, kanst du Variablen in so einer Session einfach mit SET setzen, ohne Declare. Aber natürlich ohne Gewähr 😉 Spricht aber auch nichts dagegen, das was du machst, in eine Stored Procedure zu verpacken, und dann aus C# (natürlich erst nach dem Test mit MySql direkt) die Stored Procedure aufzurufen, das halte ich sowieso für besser.

D
dahaack Themenstarter:in
47 Beiträge seit 2011
vor 12 Jahren

Hallo Coder,

der obige Befehl

INSERT INTO tableUserName(userId, name, startDate)
SELECT tableUserName.id, 'Neuer Benutzer', Now()
FROM tableUserName
WHERE tableUserName.name = 'Benutzer 2'

funktioniert ja bereits, und viel eleganter kann man das glaube ich nicht machen. Daher ist das Problem ja sowieso gelöst. Du hast wahrscheinlich recht, dass eine Variablendeklaration an dieser Stelle nicht möglich ist.

Ich hab nochmal eine allgemeine Frage zu den beiden Tabellen. Ich möchte eine VIEW erstellen, die mir alle aktuellen Benutzernamen anzeigt. Dafür hab ich zwei Funktionen erstellt. "GetLastUserNameStartDate" gibt die letzte Namensänderung einer userId zurück und "GetUserName" gibt schließlich den aktuellen Benutzernamen einer userId zurück.

CREATE FUNCTION GetLastUserNameStartDate(userId INT UNSIGNED)
RETURNS DATETIME
BEGIN
	DECLARE startDate DATETIME;
	SELECT MAX(tableUserName.startDate)
	FROM tableUserName
	WHERE tableUserName.userId = userId
	INTO startDate;
	RETURN startDate;
END;

CREATE FUNCTION GetUserName (userId INT UNSIGNED)
RETURNS CHAR(48)
BEGIN
	DECLARE name CHAR(48);
	SELECT tableUserName.name
	FROM tableUserName
	WHERE tableUserName.startDate = GetLastUserNameStartDate(userId)
	AND tableUserName.userId = userId
	INTO name;
	RETURN name;
END;

CREATE VIEW viewUser AS
	SELECT tableUserName.name, tableUsername.startDate, tableUserName.userId
	FROM tableUserName, tableUser
	WHERE tableUserName.userId = tableUser.id
	AND tableUserName.name = GetUserName(tableUser.id)
	AND tableUserName.startDate = GetLastUserNameStartDate(tableUser.id);

Diese Sicht sieht allerdings extrem ineffizient aus. (Zum Beispiel wird zwei mal "GetLastUserNameStartDate" mit dem gleichen Parameter aufgerufen)

Bei einer so komplexen Rechenaufgabe für so selten ändernder Werte würde ich da schon eher eine zusätzliche Tabelle erstellen mit den aktuellen Benutzernamen. Aber sowas ist ja streng verboten. 😉

Überseh ich da einen einfacheren Weg?

D
dahaack Themenstarter:in
47 Beiträge seit 2011
vor 12 Jahren

Okay, hat sich erledigt.

CREATE FUNCTION f_IsActualUserName ( userId INT UNSIGNED, userName CHAR(48), nameStartDate DATETIME )
RETURNS BOOL
BEGIN
	DECLARE date DATETIME;
	DECLARE name CHAR(48);

	SELECT MAX(tableUserName.startDate)
	FROM tableUserName
	WHERE tableUserName.userId = userId
	INTO date;

	IF date != nameStartDate THEN
		RETURN FALSE;
	END IF;

	SELECT tableUserName.name
	FROM tableUserName
	WHERE tableUserName.startDate = date
	AND tableUserName.userId = userId
	INTO name;

	RETURN userName = name;
END;


CREATE VIEW v_User AS
	SELECT tableUserName.name, tableUsername.startDate, tableUserName.userId
	FROM tableUserName, tableUser
	WHERE tableUserName.userId = tableUser.id
	AND f_IsActualUserName(tableUserName.userId, tableUserName.name, tableUserName.startDate);

Danke an alle!

Grüße

dahaack