[MySQL] INSERT IF NOT EXISTS

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • IMarks
  • Registratie: Juli 2011
  • Laatst online: 21:25
Huidige status
MySQL:
1
2
3
INSERT INTO acl (rol,toegang,resource_id)
SELECT '4','deny',NULL from acl
WHERE (SELECT COUNT(id) as total FROM acl WHERE rol='4' AND toegang='deny' AND resource_id IS NULL) = 0 LIMIT 1;

Alleen de array moet uitgelezen kunnen worden key='value', key='value', key='value'



Lang verhaal kort
Ik ben opzoek naar een query die alleen insert als de volledige rij uniek is.
de gegevens worden alsvolgt aangeleverd in me model.
PHP:
1
2
3
4
5
$data = array(
    'rol'           => $value->getRol(),
    'toegang'       => $value->getToegang(),
    'resource_id'   => $value->getResource_Id()
);



Uitgebreid verhaal
Ik ben bezig met een insert query die alleen moet toevoegen op moment dat een volledige rij niet gelijk is aan een die al bestaat.

De database is als volgt te zien

IdRolToegangResource_Id
12(admin)allow1(index)
22(admin)allow1(index)


Bovenstaande wil ik dus voorkomen dat er twee keer een rij voorkomt die 2, allow, 1 bevat.
Het probleem is echter dat ik geen INSERT IGNORE INTO kan gebruiken en tevens geen
ON DUPLICATE KEY UPDATE omdat er geen veld wordt mee gegeven die uniek is los van ID, maar die wordt bij een save functie dus niet meegegeven.

Ik maak gebruik van een framework die de gegevens als volgt aan leverd:
PHP:
1
2
3
4
5
$data = array(
    'rol'           => $value->getRol(),
    'toegang'       => $value->getToegang(),
    'resource_id'   => $value->getResource_Id()
);


op dit moment is mijn query en gegevens verwerking alsvolgt maar dit kijkt nog niet of de rij uniek is
PHP:
1
2
3
4
5
6
7
8
9
10
11
$keys   = array_keys($data);
$values = array_values($data);
        
$keys   = implode(",", $keys);
$values = implode("','", $values);

$select = $this->getDbTable()->getAdapter()->query('INSERT INTO acl
                                                    ('. $keys . ')
                                                    VALUES (\'' . $values . '\')
                                                    ');
return $select;


De enige oplossing die ik op dit moment kan bedenken is om in de model een losse query te plaatsen
die controleerd of die een 1 rij (of meer) tegenkomt en zoja niks uitvoert en zo nee me origenele insert query doet.
Echter weet ik niet hoe ik de array kan omzetten naar
MySQL:
1
WHERE rol = '2' AND toegang='allow' AND resource_id = NULL


Overige details voor als je wilt helpen tot het einde
Alle drie de velden kunnen leeg zijn. Voorbeeld hiervan is
rol = null, toegang = allow, resource_id = 1. Bij een dergelijk record wordt aan iedereen toegang gegeven tot de index.
Het is dus handig om in de query al rekening te houden met dat het feit dat een volgende velden ook niet uniek zijn
IdRolToegangResource_Id
12(admin)allow1(index)
2allow1(index)

[ Voor 5% gewijzigd door IMarks op 20-11-2012 16:55 ]


Acties:
  • 0 Henk 'm!

  • Aapje
  • Registratie: Maart 2003
  • Laatst online: 09-07 19:25

Aapje

Opel-beun
left outer join op zichzelf, waar het tabel wat je joint null terug geeft?

Acties:
  • 0 Henk 'm!

  • BtM909
  • Registratie: Juni 2000
  • Niet online

BtM909

Watch out Guys...

Waarom maak je geen rol(e) aan voor iedereen ipv null?

Als de hele rij uniek moet zijn (en het gaat maar om drie velden), waarom geen (composite) PK op die velden?

Ace of Base vs Charli XCX - All That She Boom Claps (RMT) | Clean Bandit vs Galantis - I'd Rather Be You (RMT)
You've moved up on my notch-list. You have 1 notch
I have a black belt in Kung Flu.


Acties:
  • 0 Henk 'm!

  • IMarks
  • Registratie: Juli 2011
  • Laatst online: 21:25
De uitvoering aka verwerking van de gegevens gebeurd in Zend Framework in hun functie Zend Acl.
Bij Zend Acl is het zo dat als iets NULL is het automatisch voor alles geldt (dit kan dus een groep zijn maar ook voor alle pagina's zijn). Het is dus makkelijk om die NULL aan te houden gezien de uiteindelijk verwerking dan om groep 'everyone' aan te maken.

En wat kan ik voorstellen bij die (composite) PK, ik heb geen flauw idee hoe ik een gehele rij als primary key kan instellen. (ik gebruik navicat, als je daar uitleg in kan geven is handig, maar commando's/query's mogen/kunnen uiteraard ook)

@aapje waar wou je die left outer join op uitvoeren dan? en left join lijkt me moeilijk te gebruiken in een insert, en een where is net zo eenvoudig daarin.

Acties:
  • 0 Henk 'm!

  • iH8
  • Registratie: December 2001
  • Laatst online: 17-06-2024

iH8

IMarks schreef op dinsdag 20 november 2012 @ 11:05:
ik heb geen flauw idee hoe ik een gehele rij als primary key kan instellen.
Every table must have a primary key. You can declare the column for the primary key using the protected variable $_primary. This is either a string that names the single column for the primary key, or else it is an array of column names if your primary key is a compound key.

Aunt bunny is coming to get me!


Acties:
  • 0 Henk 'm!

  • IMarks
  • Registratie: Juli 2011
  • Laatst online: 21:25
Ik heb nu inderdaad alles een primary key gemaakt, echter worden de NULL velden veranderd in 0.
Dit is niet zo zeer een probleem want dat kan ik "modify'en" maar zodra ik een insert null, 0 of '0' doet krijg ik de error:
MySQL:
1
2
3
cannot add or update a child row: a foreign key constraint fails 
(`jbnb`.`acl`, CONSTRAINT `fk_acl_resource1` FOREIGN KEY (`resource_id`)
REFERENCES `resources` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION)

Acties:
  • 0 Henk 'm!

  • MueR
  • Registratie: Januari 2004
  • Laatst online: 19:35

MueR

Admin Tweakers Discord

is niet lief

Hoewel het in vrijwel alle gevallen wel klopt wat je quote, hoeft niet elke tabel een PK te hebben. Bij een simpele koppeltabel bijvoorbeeld is het onzin om daar een extra kolom in te gooien. Dan kan je prima volstaan met een UNIQUE op beide velden.

Anyone who gets in between me and my morning coffee should be insecure.


Acties:
  • 0 Henk 'm!

  • T-MOB
  • Registratie: Maart 2001
  • Laatst online: 23:45
Als je een UNIQUE constraint op de rijen kolommen legt die uniek moeten zijn kun je wel met ON DUPLICATE KEY UPDATE werken. Dan kun je de PK op het `id` laten liggen en AUTO_INCREMENT blijven gebruiken (even vooropgesteld dat je dat wil).

Maar als er kolommen NULLable zijn dan heb je er niet zo heel veel aan. Want NULL is ongelijk aan NULL, dus
(2,allow,NULL) != (2,allow,NULL).

Je kunt dat oplossen door specifieke waarden te nemen voor de zaken waar je nu NULL voor misbruikt. Een ANY-rol met een specifiek id (bijvoorbeeld "0"). Een toegang "all" (of "none", net wat je nu met NULL impliceert) en een "ALL"-resource (of "None", wederom afhankelijk wat je nu uit NULL afleidt).

Regeren is vooruitschuiven


Acties:
  • 0 Henk 'm!

  • Dentist
  • Registratie: December 2000
  • Laatst online: 09-07 17:41

Dentist

Next patient please...

misschien een beetje een hack, maar waarom hash je de gegevens niet en gebruik je dat niet als een identifier?

Acties:
  • 0 Henk 'm!

  • Otherside1982
  • Registratie: Februari 2009
  • Laatst online: 10-07 08:30
Met iets als insert ... select ... is dat toch eenvoudig te doen.

Een andere mogelijkheid is een unique key/index leggen op alle velden en insert ignore ... gebruiken.

[ Voor 38% gewijzigd door Otherside1982 op 20-11-2012 14:50 ]


Acties:
  • 0 Henk 'm!

  • IMarks
  • Registratie: Juli 2011
  • Laatst online: 21:25
ik kan het verkeerd hebben maar het lijk mij dat die query insert op het moment dat er juist een record (of meer) wordt gevonden.

Acties:
  • 0 Henk 'm!

  • JaQ
  • Registratie: Juni 2001
  • Laatst online: 09-07 22:43

JaQ

@TS: als je geen unique key op iets anders dan je PK hebt liggen, hoe kan je RDBMS dan "bedenken" dat je een duplicate value hebt? (want dat is toch exact wat je vraagt?)

Exact zoals hier wordt voorgesteld:
T-MOB schreef op dinsdag 20 november 2012 @ 13:28:
Als je een UNIQUE constraint op de rijen kolommen legt die uniek moeten zijn kun je wel met ON DUPLICATE KEY UPDATE werken. Dan kun je de PK op het `id` laten liggen en AUTO_INCREMENT blijven gebruiken (even vooropgesteld dat je dat wil).

Egoist: A person of low taste, more interested in themselves than in me


Acties:
  • 0 Henk 'm!

  • IMarks
  • Registratie: Juli 2011
  • Laatst online: 21:25
Ik heb de velden als combinatie UNIQUE gemaakt, zie de kopie van de index:
MySQL:
1
fk_acl_combined1    `rol`, `toegang`, `resource_id` Unique  0   A   0   4   0       0           

maar als ik nu de volgende query uitvoer krijg ik alsnog een dubbel resultaat.
MySQL:
1
2
INSERT INTO acl (rol,toegang,resource_id) VALUES ('4','allow',NULL)
ON DUPLICATE KEY UPDATE rol = '4', toegang='allow', resource_id=NULL

Acties:
  • 0 Henk 'm!

  • P.O. Box
  • Registratie: Augustus 2005
  • Niet online
IMarks schreef op dinsdag 20 november 2012 @ 15:34:
Ik heb de velden als combinatie UNIQUE gemaakt, zie de kopie van de index:
MySQL:
1
fk_acl_combined1    `rol`, `toegang`, `resource_id` Unique  0   A   0   4   0       0           

maar als ik nu de volgende query uitvoer krijg ik alsnog een dubbel resultaat.
MySQL:
1
2
INSERT INTO acl (rol,toegang,resource_id) VALUES ('4','allow',NULL)
ON DUPLICATE KEY UPDATE rol = '4', toegang='allow', resource_id=NULL
dat komt omdat een NULL apart gedrag vertoont bij een unique index...
misschien kun je van NULL dan beter 0 of -1 maken, om dat probleem te voorkomen?
alhoewel dat niet de schoonheidsprijs verdient m.i.

Acties:
  • 0 Henk 'm!

  • IMarks
  • Registratie: Juli 2011
  • Laatst online: 21:25
Jammer genoeg is dat helaas niet mogelijk want die null van de resource id (en soms van de rol) is een foreign key, resource '0' of '-1' bestaat dus niet.

Acties:
  • 0 Henk 'm!

  • T-MOB
  • Registratie: Maart 2001
  • Laatst online: 23:45
Dan maak je een resource "anything"... (en die kun je prima id 0 toekennen). Hetzelfde geldt trouwens voor rol en permissie. Een rol "anybody" en een permissie "*" zijn veel duidelijker dan NULL en NULL. Maar dat had ik om 13:28u ook geschreven.

[ Voor 85% gewijzigd door T-MOB op 20-11-2012 16:07 ]

Regeren is vooruitschuiven


Acties:
  • 0 Henk 'm!

  • P.O. Box
  • Registratie: Augustus 2005
  • Niet online
met dit zou het toch moeten lukken... evt. nog in combinatie met een tijdelijke tabel als mysql gaat mouwen dat een insert en een select niet tegelijk op dezelfde tabel mogen zijn (weet niet of dat in alle versies van mysql zo is namelijk, misschien dat jouw versie het geen probleem vind)

Acties:
  • 0 Henk 'm!

  • IMarks
  • Registratie: Juli 2011
  • Laatst online: 21:25
Deze query is het uiteindelijk geworden.
moet alleen nog een manier vinden om de array om te zetten tot key = 'value', key= 'value'

MySQL:
1
2
3
INSERT INTO acl (rol,toegang,resource_id)
SELECT '4','deny',NULL from acl
WHERE (SELECT COUNT(id) as total FROM acl WHERE rol='4' AND toegang='deny' AND resource_id IS NULL) = 0 LIMIT 1;

Acties:
  • 0 Henk 'm!

  • BtM909
  • Registratie: Juni 2000
  • Niet online

BtM909

Watch out Guys...

MueR schreef op dinsdag 20 november 2012 @ 13:28:
[...]

Hoewel het in vrijwel alle gevallen wel klopt wat je quote, hoeft niet elke tabel een PK te hebben. Bij een simpele koppeltabel bijvoorbeeld is het onzin om daar een extra kolom in te gooien. Dan kan je prima volstaan met een UNIQUE op beide velden.
Dat is idd een MySQL oplossing die ook nog handig is :)

Ace of Base vs Charli XCX - All That She Boom Claps (RMT) | Clean Bandit vs Galantis - I'd Rather Be You (RMT)
You've moved up on my notch-list. You have 1 notch
I have a black belt in Kung Flu.

Pagina: 1