Я нетерпелив или неэффективен? - PullRequest
0 голосов
/ 11 апреля 2019

Итак, подведите итог: у меня есть база данных sqlServer (использующая SSMS для работы с ней), и у нее есть таблица со столбцом order_num , а затем столбец description . например, 16548221587 | Small hairbrush. У меня есть индекс в столбце order_num .

Затем у меня есть приложение VB.net, которое, по сути, я хочу, чтобы оно позволяло пользователю помещать файл .txt с огромным списком order_nums (> 150 000, 1 на строку) и что оно делает. Он читает эти строки построчно, а затем выполняет поиск в базе данных, добавляет их все во временную таблицу, а затем streamwrites в файл «Results» .txt.

Что касается названия этого вопроса, я его задаю, потому что мой код, который я опубликую ниже, работает! и я проверяю это при чтении и нахожу и вставляю каждую находку в временную таблицу при .427 секундах поиска, но ставлю это с 150 000 записей, на это требуется более 16 часов! Так вот, мне интересно, делаю ли я этот переворот или я ожидаю слишком много чтения / поиска и извлечения такого количества записей и ожидаю, что это пойдет быстрее?

If System.IO.File.Exists(TextBox2.Text) Then
                    'read in file list of order numbers to search
                    result = From n In System.IO.File.ReadLines(TextBox2.Text)
                             Where n.Length = 13
                             Select n.Substring(0, 13)
                Else
                    MessageBox.Show("The file path you entered seems to be invalid. Please try again.")
                    Exit Sub
                End If



                For Each word As String In result

                    Dim cmd As New SqlCommand("dbo.OrdersToTemp", con)
                    cmd.CommandType = CommandType.StoredProcedure
                    cmd.Parameters.Add("@OrderNum", SqlDbType.NVarChar).Value = word.ToString()
                    cmd.CommandTimeout = 3000
                    cmd.ExecuteNonQuery()

                Next




                Using sw As New StreamWriter(TextBox2.Text.Substring(0, TextBox2.TextLength - 4) + "-results.txt")


                    Dim retrieveResults As New SqlCommand("dbo.GetResults", con)
                    retrieveResults.CommandType = CommandType.StoredProcedure
                    retrieveResults.CommandTimeout = 3000

                    Using RDR = retrieveResults.ExecuteReader

                        Do While RDR.Read



                            OrderDescription = RDR("Description").ToString()

                            sw.WriteLine(OrderDescription )

                        Loop

                    End Using




                End Using

UPDATE

Я воспользовался некоторыми советами по этому поводу, и теперь я решил скопировать порядковые номера, которые необходимо найти в таблице Temp, это делается довольно быстро. Затем я использую запрос, такой как

SELECT o.order_num, description
from OrderTable o
join TempOrderIdTable t on t.order_num = o.order_num

Но все еще кажется довольно медленным, получение даже 170 результатов занимает около 30 секунд, что на мой взгляд довольно медленно. Я поместил кластерный индекс в order_num в order_table, а NO - в таблицу temp, которая в основном представляет собой просто файл .txt, за исключением таблицы sql

ОБНОВЛЕНИЕ 2

Итак, как я уже сказал, у меня теперь есть некластеризованный индекс (orderNo включает описание) в OrderTable и кластеризованный индекс (Order_num) в TempTable, НО для любого вида соединения или перекрестного применения и т. Д. Для того, чтобы в основном присоединиться, требуется еще более 33 секунд 100 OrderNum и вернуть только 170, что все еще так медленно. Вот соединения, которые я пытаюсь:

select o.Order_num, t.description
from Temp_data o
join OrderTable on t.Order_num= o.Order_num


select x.Order_num, x.description
from OrderTable x
where Order_num in (select Order_num from Temp_data)


select x.Order_num,x.description
from OrderTable x
cross apply (select o.Order_num from Temp_data o where o.Order_num= x.Order_num) ord

решаемые Так что я был идиотом для последнего бита, и вы все были в основном правы, когда я делал временную таблицу, я случайно сделал Column nvarchar, тогда как в реальном OrderTable это был просто столбец varchar для order_num. Извините за то, что я, ребята, полусонный!

Ответы [ 4 ]

3 голосов
/ 11 апреля 2019

Проблема с вашим кодом заключается в том, что вы выполняете эту команду SQL 150 раз. Это никогда не сработает (быстро).

Что вы можете сделать, это сначала прочитать 150k значений из файла, а затем вставить, используя SqlBulkCopy в таблицу, например, , как показано в этом ответе SO . По сути, делайте то, что делает ваша dbo.OrdersToTemp процедура, в вашем коде vb.net - все сразу, а не по одной строке за раз. Это должно занять максимум пару секунд, учитывая задержку для вашего текущего запроса.

Для следующего запроса:

SELECT o.order_num, description
from OrderTable o
join TempOrderIdTable t on t.order_num = o.order_num

Я предполагаю, что OrderTable может содержать несколько записей на order_num (вы упомянули возвращение 170 строк для 100 заказов), вы можете использовать индексы как таковые:

CREATE INDEX ix ON OrderTable (order_num) INCLUDE (description)

Если номера заказов в файле уникальны (и вы можете гарантировать уникальность):

CREATE UNIQUE CLUSTERED INDEX ux ON TempOrderIdTable (order_num);

Если ваша редакция SQL Server поддерживает его, вы можете сжать индекс с WITH (DATA_COMPRESSION = PAGE), но для этого требуется лицензия Enterprise (или Developer, но вы не можете использовать ее в среде prod).

1 голос
/ 11 апреля 2019

Только для дальнейшего использования (массовое копирование и индексы, безусловно, являются ответом) ... Не создавайте команду для каждой итерации цикла, добавляя параметры для каждой итерации и устанавливая свойства команды.Они одинаковы для каждой петли;изменяются только значения параметров.

    Dim cmd As New SqlCommand("dbo.OrdersToTemp", con)
    cmd.CommandType = CommandType.StoredProcedure
    cmd.Parameters.Add("@OrderNum", SqlDbType.NVarChar)
    cmd.CommandTimeout = 3000
    For Each word As String In Result
        cmd.Parameters("@OrderNum").Value = word
        cmd.ExecuteNonQuery()
    Next
1 голос
/ 11 апреля 2019

Я бы предложил следующие варианты оптимизации.

Во-первых, Bulk insert во временную таблицу идентификаторов заказов прямо из txt-файла. Если у вас есть один order_num в каждой строке, то это будет делать.

BULK INSERT TempOrderIdTable
FROM 'C:\data\orderids.txt'
WITH (FIRSTROW = 1,
    FIELDTERMINATOR = ',',
    ROWTERMINATOR='\n' );

Чтобы bulk insert работал, файл должен быть доступен для сервера SQL либо на компьютере с сервером SQL, либо через общее местоположение в сети.

Во-вторых, запустите запрос, чтобы получить все описания за один раз, присоединившись к таблице временных идентификаторов заказа:

SELECT o.order_num, description
from OrderTable o
join TempOrderIdTable t on t.order_num = o.order_num

Теперь у вас есть результаты в два этапа.

1 голос
/ 11 апреля 2019

Я не знаю структуру хранимых процедур, которые вы используете в команде, но я предполагаю, что большую часть времени операции на сервере SQL и обмен данными между приложением и сервером SQL занимают большую часть времени,Так что подумайте об оптимизации - как отправить все цифры сразу на процедуру.Вам не придется вызывать процедуру xy раз.И попытка взглянуть на план выполнения хранимых процедур - есть ли что-то, что можно улучшить?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...