Как я эффективно делаю объем, вставлять-или-обновлять с SQLAlchemy?

Проблема уменьшает до этого вопроса: две строки от до B и от C до D пересекаются? Затем можно спросить это четыре раза (между строкой и каждой из четырех сторон прямоугольника).

Вот векторная математика для того, чтобы сделать его. Я предполагаю, что строка от до B является рассматриваемой строкой, и строка от C до D является одной из прямоугольных строк. Моя нотация - то, что Ax "x-координата", и Cy "y-координата C.". И" *" означает скалярное произведение, так например, A*B = Ax*Bx + Ay*By.

E = B-A = ( Bx-Ax, By-Ay )
F = D-C = ( Dx-Cx, Dy-Cy ) 
P = ( -Ey, Ex )
h = ( (A-C) * P ) / ( F * P )

Этот h число является ключом. Если h между 0 и 1, строки пересекаются, иначе они не делают. Если F*P нуль, конечно, Вы не можете сделать вычисление, но в этом случае строки параллельны, и поэтому только пересекитесь в очевидных случаях.

точная точка пересечения C + F*h.

[еще 1141] Забава:

, Если h точно 0 или 1, строки заходят в конечную точку. Можно считать это "пересечением" или не, как Вы считаете целесообразным.

А именно, h то, насколько необходимо умножить длину строки для точного касания другой строки.

Поэтому, Если h<0, это означает, прямоугольная строка находится "позади" данной строки (при этом "направление" "от до B"), и если h>1 прямоугольная строка имеет "впереди" данную строку.

Деривация:

А и C являются векторами, которые указывают на запуск строки; E и F являются векторами от концов A и C, которые формируют строку.

Для любых двух непараллельных строк в плоскости, должна быть точно одна пара скаляра g и h таким образом, что это уравнение содержит:

A + E*g = C + F*h

, Почему? Поскольку две непараллельных строки должны пересечься, что означает, что можно масштабировать обе строки некоторой суммой каждый и коснуться друг друга.

( Сначала это похоже на единственное уравнение с двумя неизвестными! , Но это не, когда Вы полагаете, что это - 2D векторное уравнение, что означает, что это - действительно пара уравнений в [1 121] и y.)

Мы должны устранить одну из этих переменных. Простой способ состоит в том, чтобы сделать E нуль термина. Чтобы сделать это, возьмите скалярное произведение обеих сторон уравнения с помощью вектора, который отметит точкой для обнуления с E. Тот вектор, который я назвал P выше, и я сделал очевидное преобразование E.

Вы теперь имеете:

A*P = C*P + F*P*h
(A-C)*P = (F*P)*h
( (A-C)*P ) / (F*P) = h
8
задан mike 25 August 2009 в 19:38
поделиться

2 ответа

Вы сталкиваетесь со странным поведением, специфичным для Postgresql: если в транзакции возникает ошибка, она заставляет откат всей транзакции. Я считаю это ошибкой дизайна Postgres; в некоторых случаях требуется немало искажений SQL, чтобы обойти эту проблему.

Один из способов решения проблемы - сначала выполнить UPDATE. Определите, действительно ли он изменил строку, посмотрев на cursor.rowcount; если он не изменил ни одной строки, значит, он не существует, так же как и INSERT. (Это будет быстрее, если вы будете обновлять чаще, чем вставляете, конечно.)

Другой обходной путь - использовать точки сохранения:

SAVEPOINT a;
INSERT INTO ....;
-- on error:
ROLLBACK TO SAVEPOINT a;
UPDATE ...;
-- on success:
RELEASE SAVEPOINT a;

Это серьезная проблема для кода производственного качества: вы должны точно определять ошибку. Предположительно вы ожидаете пройти проверку уникального ограничения, но вы можете столкнуться с чем-то неожиданным, и может оказаться почти невозможным надежно отличить ожидаемую ошибку от неожиданной. Если это неправильно попадает в состояние ошибки, это приведет к неясным проблемам, когда ничего не будет обновлено или вставлено, и ошибка не будет видна. Будьте с этим очень осторожны. Вы можете сузить количество случаев ошибки, просмотрев код ошибки Postgresql, чтобы убедиться, что это именно тот тип ошибки, который вы ожидаете, но потенциальная проблема все еще существует.

Наконец, если вы действительно хотите выполнить пакетную вставку или- update, вы действительно хотите выполнить многие из них в нескольких командах, а не по одному элементу на команду. Для этого требуется более сложный SQL: SELECT, вложенный в INSERT, отфильтровывающий нужные элементы для вставки и обновления.

Это приведет к неясным проблемам, когда ничего не будет обновлено или вставлено, и никаких ошибок не будет. Будьте с этим очень осторожны. Вы можете сузить количество случаев ошибки, просмотрев код ошибки Postgresql, чтобы убедиться, что это именно тот тип ошибки, который вы ожидаете, но потенциальная проблема все еще существует.

Наконец, если вы действительно хотите выполнить пакетную вставку или- update, вы действительно хотите выполнить многие из них в нескольких командах, а не по одному элементу на команду. Для этого требуется более сложный SQL: SELECT, вложенный в INSERT, отфильтровывающий нужные элементы для вставки и обновления.

Это приведет к неясным проблемам, когда ничего не будет обновлено или вставлено, и никаких ошибок не будет. Будьте с этим очень осторожны. Вы можете сузить количество случаев ошибки, просмотрев код ошибки Postgresql, чтобы убедиться, что это именно тот тип ошибки, который вы ожидаете, но потенциальная проблема все еще существует.

Наконец, если вы действительно хотите выполнить пакетную вставку или- update, вы на самом деле хотите выполнить многие из них в нескольких командах, а не по одному элементу на команду. Для этого требуется более сложный SQL: SELECT, вложенный в INSERT, отфильтровывающий нужные элементы для вставки и обновления.

но потенциальная проблема все еще существует.

Наконец, если вы действительно хотите выполнить пакетную вставку или обновление, вы действительно захотите выполнить многие из них в нескольких командах, а не по одному элементу на команду. Для этого требуется более сложный SQL: SELECT, вложенный в INSERT, отфильтровывающий нужные элементы для вставки и обновления.

но потенциальная проблема все еще существует.

Наконец, если вы действительно хотите выполнить пакетную вставку или обновление, вы действительно хотите выполнить многие из них в нескольких командах, а не по одному элементу на команду. Для этого требуется более сложный SQL: SELECT, вложенный в INSERT, отфильтровывающий нужные элементы для вставки и обновления.

5
ответ дан 5 December 2019 в 19:01
поделиться

Эта ошибка возникла в PostgreSQL. PostgreSQL не позволяет выполнять команды в одной транзакции, если одна команда вызывает ошибку. Чтобы исправить это, вы можете использовать вложенные транзакции (реализованные с использованием точек сохранения SQL) через conn.begin_nested () . Вот кое-что, что может сработать. Я заставил код использовать явные соединения, исключил часть фрагментации и заставил код использовать диспетчер контекста для правильного управления транзакциями.

from itertools import chain, islice
def chunked(seq, chunksize):
    """Yields items from an iterator in chunks."""
    it = iter(seq)
    while True:
        yield chain([it.next()], islice(it, chunksize-1))

conn = engine.commit()
for chunk in chunked(records, 1000):
    with conn.begin():
        for rec in chunk:
            try:
                with conn.begin_nested():
                     conn.execute(inserter, ...)
            except sa.exceptions.SQLError:
                conn.execute(my_table.update(...))

Это все равно не будет иметь звездной производительности, хотя из-за накладных расходов на вложенные транзакции. Если вам нужна более высокая производительность, попробуйте заранее определить, какие строки будут создавать ошибки, с помощью запроса на выбор и используйте поддержку executemany (выполнение может принимать список слов, если все вставки используют одни и те же столбцы). Если вам нужно обрабатывать одновременные обновления, вам все равно придется выполнять обработку ошибок, повторяя попытки или возвращаясь к одной за другой вставки.

4
ответ дан 5 December 2019 в 19:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: