Dodaj do tabeli, jeśli warunek nie jest spełniony

0

Witajcie,

Poniższa kwerenda, doda rekord do tabeli, jeśli warunek zostanie spełniony.

INSERT INTO table (field1, field2, field3)
SELECT 'field 1', 'field 2', 'field 3'
FROM table
WHERE (field1 = 'A' AND field2 = 'B');

Chciałbym zanegować warunek i dodać rekord, kiedy nie jest on spełniony.

Jeśli użyję WHERE NOT, to do tabeli dodawanych jest n nowych rekordów, bez tego jednego, jeśli już istniał.

Serdecznie dziękuję za pomoc.

0

Musisz użyć funkcji / języka proceduralnego który sprawdzi czy dla danych wartości istnieje rekord, i jeżeli nie istnieje to dodaje do tabeli.

Jedną komendą SQL raczej tego nie załatwisz - chyba, że są jakieś tego typu triki w silniku bazy danych który używasz, i to jest pierwsza informacja jaką powinieneś podać - co to jest: MSSQL, PostgreSQL, Informix, etc?

Ewentualnie jakieś inteligentne użycie COLEASCE mogłoby pomóc.

0
TomRZ napisał(a):

Musisz użyć funkcji / języka proceduralnego który sprawdzi czy dla danych wartości istnieje rekord, i jeżeli nie istnieje to dodaje do tabeli.

Jedną komendą SQL raczej tego nie załatwisz - chyba, że są jakieś tego typu triki w silniku bazy danych który używasz, i to jest pierwsza informacja jaką powinieneś podać - co to jest: MSSQL, PostgreSQL, Informix, etc?

Ewentualnie jakieś inteligentne użycie COLEASCE mogłoby pomóc.

Domyślałem się, że chyba tego tak nie zrobię, ale poczekam jeszcze, może jednak ktoś coś wymyśli.

Używam SQLite dla C# (System.Data.SQLite; SQLite format 3).

1

Jeżeli dobrze rozumiem, to jeżeli masz masz rekord spełniajacy te kryteria to nie dodajesz nic

 select 'field 1', 'field 2', 'field 3'  from (
select *,EXISTS(select * from tab where a="a" and b="b") i from tab)
where i = 0
limit 1

https://www.db-fiddle.com/f/fNhxXZBf3iTekRnK1s2ZKk/0

0

@TomRZ: odpowiadam w poście bo w komentarzu się nie zmieszczę.

TomRZ napisał(a):

Musisz użyć funkcji / języka proceduralnego który sprawdzi czy dla danych wartości istnieje rekord, i jeżeli nie istnieje to dodaje do tabeli.

Czyli np w mssql tak

If not exists (SELECT * FROM table WHERE (field1 = 'A' AND field2 = 'B'))
Begin
Insert into table values ('field 1', 'field 2', 'field 3')
End

Moja propozycja zadziała dokładnie tak samo

Insert into tab
select 'field 1', 'field 2', 'field 3'  from (
select *,EXISTS(select * from tab where a="a" and b="b") i from tab)
where i = 0
limit 1

Czego nie zrozumiałem?

1

Najprościej Not exist lub not in. Ewentualnie where 1=0.

0
Panczo napisał(a):

Jeżeli dobrze rozumiem, to jeżeli masz masz rekord spełniajacy te kryteria to nie dodajesz nic

 select 'field 1', 'field 2', 'field 3'  from (
select *,EXISTS(select * from tab where a="a" and b="b") i from tab)
where i = 0
limit 1

https://www.db-fiddle.com/f/fNhxXZBf3iTekRnK1s2ZKk/0

Witajcie,

Przepraszam, jeśli moje pytanie było niejasne, ale tak @TomRZ, @Panczo dobrze zinterpretował moje pytanie.

Gdybym miał zapisać to o co mi chodzi w pseudokodzie:

if ( (field1 != 'A') || (field2 != 'B') ) // --> !((field1 == 'A') && (field2 == 'B'))
    InsertIntoTable(table, "field 1", "field 2", "field 3");

Panczo zawarł to w bloku Begin - End oraz w samej kwerendzie.

Międzyczasie, udało mi się samemu dojść do odpowiedzi, która przynajmniej na moje oko działa i o której chyba wspomina @kate87 (chociaż, nie wiem czy to miała na myśli):

INSERT INTO table (field1, field2, field3)
SELECT 'field 1', 'field 2', 'field 3'
FROM table
WHERE NOT EXISTS
(
    SELECT 1
    FROM table
    WHERE (field1 = 'A' AND field2 = 'B')
)
LIMIT 1

kate87, czy mógłbym Cię prosić o sprecyzowanie, jak można byłoby jeszcze użyć NOT IN lub WHERE 1 = 0, jak sugerujesz?

Dziękuję

0

Może napisz jasno o co Ci chodzi:

  1. o to żeby dodać do tabeli A wszystkie rekordy z tabeli B poza wybranym(i)?
  2. o to żeby dodać do tabeli A wartości, które są nieobecne w tabeli B, i nie dodawać nic innego?

Coś niedobrego dzieje się ze szkołą podstawową, jeżeli ludzie nie potrafią nawet logicznie przedstawić o co im chodzi. Tym drugim postem namieszałeś jeszcze bardziej.

1
TomRZ napisał(a):

Może napisz jasno o co Ci chodzi:

  1. o to żeby dodać do tabeli A wszystkie rekordy z tabeli B poza wybranym(i)?
  2. o to żeby dodać do tabeli A wartości, które są nieobecne w tabeli B, i nie dodawać nic innego?

Coś niedobrego dzieje się ze szkołą podstawową, jeżeli ludzie nie potrafią nawet logicznie przedstawić o co im chodzi. Tym drugim postem namieszałeś jeszcze bardziej.

Myślę, że skoro Panczo zrozumiał za pierwszym razem to nie było tak źle.
Jak pisałem:

ProNOP napisał(a):

Chciałbym zanegować warunek i dodać rekord, kiedy nie jest on spełniony.

W ostatniej mojej odpowiedzi napisałem, że rezultat działania kodu Panczo i mojego, robi dokładnie to o co mi chodziło - dodałem również pseudokod, który obrazuje dokładnie to samo.

if ( (field1 != 'A') || (field2 != 'B') ) // --> !((field1 == 'A') && (field2 == 'B'))
    InsertIntoTable(table, "field 1", "field 2", "field 3");

To samo napisał Panczo tylko w notacji MySQL.

Obawiam się Przyjacielu, że, choćby to, cytuję:

TomRZ napisał(a):

Może napisz jasno o co Ci chodzi:

  1. o to żeby dodać do tabeli A wszystkie rekordy z tabeli B poza wybranym(i)?

świadczy o niezrozumieniu nawet nie tylko samego pytania, ale i tego co przedstawiłem, gdyż nigdzie nie wspominałem o dwóch różnych tabelach, a o jednej tabeli - popatrz proszę na pierwszy mój post i kod SQL.

TomRZ napisał(a):

Coś niedobrego dzieje się ze szkołą podstawową, jeżeli ludzie nie potrafią nawet logicznie przedstawić o co im chodzi. Tym drugim postem namieszałeś jeszcze bardziej.

Szkołę podstawową już dawno mam za sobą Przyjacielu.

Dziękuję

0

Sądziłem, że chodzi o dwie różne tabele, bo w głowie mi się nie mieści, że można to tak niemądrze robić. Pomyśl, co się stanie kiedy równocześnie będziesz miał uruchomione dwa takie zapytania z identycznymi wartościami.

W bazach danych zazwyczaj w takim przypadku są specjalne klauzule typu "INSERT ... ON CONFLICT DO NOTHING/UPDATE"

jeżeli czegoś podobnego nie ma w SQLite, to na dobrą sprawę aby Ci to działało prawidłowo w wielowątkowym środowisku, to musisz:

  • zablokować całą tabelę SQL (nie da się tego zrobić na poziomie jednego wiersza) - o ile tak się da w SQLite
  • przeprowadzić operację
  • odblokować tabelę SQL

Ewentualnie zrobić synchro na poziomie aplikacji C#

0
TomRZ napisał(a):

Sądziłem, że chodzi o dwie różne tabele, bo w głowie mi się nie mieści, że można to tak niemądrze robić.

Nie jestem nieomylny Przyjacielu.

TomRZ napisał(a):

Pomyśl, co się stanie kiedy równocześnie będziesz miał uruchomione dwa takie zapytania z identycznymi wartościami.

Czy chodzi Ci o te zapytanie zaproponowane przez Panczo:

Panczo napisał(a):
 select 'field 1', 'field 2', 'field 3'  from (
select *,EXISTS(select * from tab where a="a" and b="b") i from tab)
where i = 0
limit 1

czy moje:

INSERT INTO table (field1, field2, field3)
SELECT 'field 1', 'field 2', 'field 3'
FROM table
WHERE NOT EXISTS
(
    SELECT 1
    FROM table
    WHERE (field1 = 'A' AND field2 = 'B')
)
LIMIT 1

a może oba?

pisząc o:

TomRZ napisał(a):

(...) z identycznymi wartościami.

które z wartości masz na myśli? te które będę dodawał, tak? bo jeśli tak, to mam zagwarantowane, że w tej tabeli, drugich takich nie będzie, ale dziękuję za trafne spostrzeżenie.

TomRZ napisał(a):

W bazach danych zazwyczaj w takim przypadku są specjalne klauzule typu "INSERT ... ON CONFLICT DO NOTHING/UPDATE"
jeżeli czegoś podobnego nie ma w SQLite, (...)

SQLite obsługuje ON CONFLICT ..., jest też dostępne INSERT OR IGNORE.

0
ProNOP napisał(a):

które z wartości masz na myśli? te które będę dodawał, tak? bo jeśli tak, to mam zagwarantowane, że w tej tabeli, drugich takich nie będzie, ale dziękuję za trafne spostrzeżenie.

Nie, nie masz tego zagwarantowanego, chyba nie rozumiesz jak działają transakcje w bazach danych.

Wystarczy, że np. Ci się palec omsknie i bardzo szybko dwa razy naciśniesz np. przycisk który ma to wykonać, w tym momencie masz dwa identyczne zapytania SQL dodające tą wartość do bazy, każde w osobnej transakcji. Pierwsze doda wartość, ale nie będzie to widoczne dla zapytania nr 2 dla którego tej wartości nadal nie ma, zapytanie nr 2 doda więc istniejącą już wartość i to spowoduje błąd / exception w aplikacji.

Dlatego stosuje się ON CONFLICT, żeby tego uniknąć, albo w inny sposób synchronizuje taką operację.

Poczytaj sobie o transakcjach w bazach danych.

No chyba, że SQLite to nie jest silnik transakcyjny i wszystko jest kolejkowane, a insert/update oznacza lock całej tabeli tak jak jest w np. w storage MyISAM dla MySQL - wtedy nie musisz się przejmować.

0
TomRZ napisał(a):

Nie, nie masz tego zagwarantowanego, chyba nie rozumiesz jak działają transakcje w bazach danych.

Nie wiesz jak wygląda logika całego projektu, a zakładasz, że na pewno masz racje.

TomRZ napisał(a):

Wystarczy, że np. Ci się palec omsknie i bardzo szybko dwa razy naciśniesz np. przycisk który ma to wykonać

Taki przykład, uznam za żart.

TomRZ napisał(a):

Dlatego stosuje się ON CONFLICT, żeby tego uniknąć, albo w inny sposób synchronizuje taką operację.

Poczytaj sobie o transakcjach w bazach danych.

No chyba, że SQLite to nie jest silnik transakcyjny i wszystko jest kolejkowane, a insert/update oznacza lock całej tabeli tak jak jest w np. w storage MyISAM dla MySQL - wtedy nie musisz się przejmować.

Myślę Przyjacielu, że zakładanie iż rozmówca jest pozbawiony wiedzy, tylko na podstawie pytania jakie zadał... rzekłbym to mocny nietakt - tak czy inaczej, dziękuję za sugestie.

0

Przy pierwszym razie kiedy współbieżność / wielowątkowość zacznie Ci robić błędy to sobie przypomnij co napisałem. O ile oczywiście dojdziesz do tego, że to jest przyczyną, bo tego typu błędy są dość paskudne w dochodzeniu przyczyn.

Zakładanie tego co sobie założyłeś, i nie robienie ochrony przed wielowątkowością to właśnie jest przyczyna tego, że się później rzeczy "wywalają" w niespodziewanych momentach.

Z tego co piszesz zresztą jasno wynika, że o transakcjach i wielowątkowości nie masz pojęcia - założyłbym się o to.

0
TomRZ napisał(a):

Okay, ale przy pierwszym razie kiedy współbieżność / wielowątkowość zacznie Ci robić błędy to sobie przypomnij co napisałem. O ile oczywiście dojdziesz do tego, że to jest przyczyną, bo tego typu błędy są dość paskudne w dochodzeniu przyczyn.

...a jak do tego jeszcze dojdzie multi-GPU i sieć to jest armagedon...

TomRZ napisał(a):

Zakładanie tego co sobie założyłeś, i nie robienie ochrony przed wielowątkowością to właśnie jest przyczyna tego, że się później rzeczy "wywalają" w niespodziewanych momentach.

To może napiszę to bardziej dosadnie: dane są tak walidowane, że gdy spływają do bazy, już są unikatowe.

TomRZ napisał(a):

Z tego co piszesz zresztą jasno wynika, że o transakcjach i wielowątkowości nie masz pojęcia - założyłbym się o to.

Przyjacielu, nie mierz innych swoją miarą... przegrałbyś.

Dziękuję

0
ProNOP napisał(a):

To może napiszę to bardziej dosadnie: dane są tak walidowane, że gdy spływają do bazy, już są unikatowe.

W takim razie po co to jeszcze raz sprawdzasz, skoro dane już są unikalne? xD

Najpierw sprawdzasz, a potem jeszcze raz sprawdzasz robiąc insert zmieszany z selectem?

Przecież to nie ma żadnego sensu co piszesz.

0
TomRZ napisał(a):
ProNOP napisał(a):

To może napiszę to bardziej dosadnie: dane są tak walidowane, że gdy spływają do bazy, już są unikatowe.

W takim razie po co to jeszcze raz sprawdzasz, skoro dane już są unikalne? xD

Najpierw sprawdzasz, a potem jeszcze raz sprawdzasz robiąc insert zmieszany z selectem?

Przecież to nie ma żadnego sensu co piszesz.

Wiesz co...? to co podałem w poście z zapytaniem to tylko przykład - chodziło mi o mechanikę - jak pisałem, nie znasz projektu, a już wszystko o nim wiesz.

Dziękuję Ci Przyjacielu za uwagi i spostrzeżenia, a teraz dajmy szansę na wypowiedzenie się innym zainteresowanym użytkownikom.

0

Tylko chyba nadal nie rozumiesz wielowątkowości i transakcji, ale dobrze nie będę Cię już męczył biedaczku. Powodzenia.

0
TomRZ napisał(a):

Tylko chyba nadal nie rozumiesz wielowątkowości i transakcji, ale dobrze nie będę Cię już męczył biedaczku. Powodzenia.

Serdecznie dziękuję.

1 użytkowników online, w tym zalogowanych: 0, gości: 1