Вообще говоря, запросы SQL, которые я пишу, возвращают неформатированные данные, и я оставляю их на уровне представления, веб-странице или в приложении Windows для форматирования данных по мере необходимости. Другие люди, с которыми я работаю, в том числе и мой начальник, будут настаивать на том, чтобы база данных была более эффективной. Я не уверен, что куплю это и верю, что, даже если бы база данных сделала это измеримым увеличением производительности, есть более веские причины, чтобы вообще этого избежать.
Например, я буду помещать свои запросы в слой доступа к данным с целью возможного повторного использования запросов, когда это возможно. Исходя из этого, я констатирую, что запросы с большей вероятностью можно будет повторно использовать, если данные остаются в своем собственном типе, а не преобразовывать данные в строку и применять к ним функции форматирования, например форматирование столбца даты в DD -МММ-ГГГГ формат для отображения. Конечно, если SQL возвращал даты в виде отформатированных строк, вы можете повернуть процесс вспять, чтобы вернуть значение обратно в тип данных date, но это кажется неудобным из-за отсутствия лучшего слова. Более того, когда дело доходит до форматирования других данных, например, серийного номера машины, состоящего из префикса, базы и суффикса с разделительными чертами и ведущими нулями, удаленными в каждом подполе, вы рискуете возможностью того, что вы не сможете правильно вернитесь к исходному серийному номеру, когда будете двигаться в другом направлении. Может быть, это плохой пример, но я надеюсь, что вы видите направление, в котором я иду с этим ...
Чтобы продвинуться дальше, я вижу, что люди пишут ОЧЕНЬ сложные SQL, потому что они, по сути, пишут то, что я бы назвал логикой представления, в SQL вместо возврата простых данных, а затем применяем эту логику представления на уровне представления. На мой взгляд, это приводит к очень сложному, сложному в обслуживании и более хрупкому SQL, который менее адаптируем к изменениям.
Возьмите следующий реальный пример того, что я нашел в нашей системе, и скажите мне, что вы думаете. Рациональный подход, который мне дали для этого подхода, заключался в том, что это сделало веб-приложение очень простым для рендеринга страницы, поскольку оно использовало следующий однострочный фрагмент классической логики ADO в классическом веб-приложении ASP для обработки возвращаемых строк:
oRS.GetString ( , , "</td>" & vbCrLf & "<td style=""font-size:x-small"" nowrap>" ,"</td>" & vbCrLf & "</tr>" & vbCrLf & "<tr>" & vbCrLf & _
"<td style=""font-size:x-small"" nowrap>" ," " ) & "</td>" & vbCrLf & "</tr>" & vbCrLf & _
Вот сам SQL. Хотя я ценю способность автора писать сложный SQL, я чувствую, что это кошмар обслуживания. Я чокнутый? SQL возвращает список программ, которые в настоящий момент работают с нашей базой данных, и статус каждой из них:
Поскольку SQL не отображался с CR / LF, когда я вставлял сюда, я решил разместить SQL на пустом персональном сайте Google. Пожалуйста, не стесняйтесь комментировать. Спасибо.
Кстати, этот SQL на самом деле был создан с использованием VB Script, вложенного в классическую страницу ASP, без вызова хранимой процедуры, поэтому у вас есть дополнительная сложность встроенных конкатенаций и разметки в кавычках, если вы знаете, что я имею в виду, а не упомянуть отсутствие форматирования. Первое, что я сделал, когда меня попросили помочь отладить SQL, - это добавить debug.print к выводу SQL и передать его через средство форматирования SQL, которое я только что нашел. Некоторая часть форматирования была потеряна при вставке по следующей ссылке:
Редактировать (Andomar): скопировано inline: (внешняя ссылка удалена, спасибо-Чад)
SELECT
Substring(Datename("dw",start_datetime),1,3)
+ ', '
+ Cast(start_datetime AS VARCHAR) "Start Time (UTC/GMT)"
,program_name "Program Name"
,run_sequence "Run Sequence"
,CASE
WHEN batchno = 0
THEN Char(160)
WHEN batchno = NULL
THEN Char(160)
ELSE Cast(batchno AS VARCHAR)
END "Batch #" /* ,Replace(Replace(detail_log ,'K:\' ,'file://servernamehere/DiskVolK/') ,'\' ,'/') "log"*/ /* */
,Cast('<a href="GOIS_ViewLog.asp?Program_Name=' AS VARCHAR(99))
+ Cast(program_name AS VARCHAR)
+ Cast('&Run_Sequence=' AS VARCHAR)
+ Cast(run_sequence AS VARCHAR)
+ Cast('&Page=1' AS VARCHAR)
+ ''
+ Cast('">'
+ CASE
WHEN end_datetime >= start_datetime
THEN CASE
WHEN end_datetime <> 'Jan 1 1900 2:00 PM'
THEN CASE
WHEN (success_code = 10
OR success_code = 0)
AND exit_code = 10
THEN CASE
WHEN errorcount = 0
THEN 'Completed Successfully'
ELSE 'Completed with Errors'
END
WHEN success_code = 100
AND exit_code = 10
THEN 'Completed with Errors'
ELSE CASE
WHEN program_name <> 'FileDepCheck'
THEN 'Failed'
ELSE 'File not found'
END
END
ELSE CASE
WHEN success_code = 10
AND exit_code = 0
THEN 'Failed; Entries for Input File Missing'
ELSE 'Aborted'
END
END
ELSE CASE
WHEN ((Cast(Datediff(mi,start_datetime,Getdate()) AS INT) <= 240)
OR ((SELECT
Count(* )
FROM
MASTER.dbo.sysprocesses a(nolock)
INNER JOIN gcsdwdb.dbo.update_log b(nolock)
ON a.program_name = b.program_name
WHERE a.program_name = update_log.program_name
AND (Abs(Datediff(n,b.start_datetime,a.login_time))) < 1) > 0))
THEN 'Processing...'
ELSE 'Aborted without end date'
END
END
+ '</a>' AS VARCHAR) "Status / Log"
,Cast('<a href="' AS VARCHAR)
+ Replace(Replace(detail_log,'K:\','file://servernamehere/DiskVolK/'),
'\','/')
+ Cast('" title="Click to view Detail log text file"' AS VARCHAR(99))
+ Cast('style="font-family:comic sans ms; font-size:12; color:blue"><img src="images\DetailLog.bmp" border="0"></a>' AS VARCHAR(999))
+ Char(160)
+ Cast('<a href="' AS VARCHAR)
+ Replace(Replace(summary_log,'K:\','file://servernamehere/DiskVolK/'),
'\','/')
+ Cast('" title="Click to view Summary log text file"' AS VARCHAR(99))
+ Cast('style="font-family:comic sans ms; font-size:12; color:blue"><img src="images\SummaryLog.bmp" border="0"></a>' AS VARCHAR(999)) "Text Logs"
,errorcount "Error Count"
,warningcount "Warning Count"
,(totmsgcount
- errorcount
- warningcount) "Information Message Count"
,CASE
WHEN end_datetime > start_datetime
THEN CASE
WHEN Cast(Datepart("hh",(end_datetime
- start_datetime)) AS INT) > 0
THEN Cast(Datepart("hh",(end_datetime
- start_datetime)) AS VARCHAR)
+ ' hr '
ELSE ' '
END
+ CASE
WHEN Cast(Datepart("mi",(end_datetime
- start_datetime)) AS INT) > 0
THEN Cast(Datepart("mi",(end_datetime
- start_datetime)) AS VARCHAR)
+ ' min '
ELSE ' '
END
+ CASE
WHEN Cast(Datepart("ss",(end_datetime
- start_datetime)) AS INT) > 0
THEN Cast(Datepart("ss",(end_datetime
- start_datetime)) AS VARCHAR)
+ ' sec '
ELSE ' '
END
ELSE CASE
WHEN end_datetime = start_datetime
THEN '< 1 sec'
ELSE CASE
WHEN ((Cast(Datediff(mi,start_datetime,Getdate()) AS INT) <= 240)
OR ((SELECT
Count(* )
FROM
MASTER.dbo.sysprocesses a(nolock)
INNER JOIN gcsdwdb.dbo.update_log b(nolock)
ON a.program_name = b.program_name
WHERE a.program_name = update_log.program_name
AND (Abs(Datediff(n,b.start_datetime,a.login_time))) < 1) > 0))
THEN 'Running '
+ Cast(Datediff(mi,start_datetime,Getdate()) AS VARCHAR)
+ ' min'
ELSE ' '
END
END
END "Elapsed Time" /* ,end_datetime "End Time (UTC/GMT)" ,datepart("hh" ,
(end_datetime - start_datetime)) "Hr" ,datepart("mi" ,(end_datetime - start_datetime)) "Mins" ,datepart("ss" ,(end_datetime - start_datetime)) "Sec" ,datepart("ms" ,(end_datetime - start_datetime)) "mSecs" ,datepart("dw" ,start_datetime) "dp" ,case when datepart("dw" ,start_datetime) = 6 then ' Fri' when datepart("dw" ,start_datetime) = 5 then ' Thu' else '1' end */
,totalrows "Total Rows"
,inserted "Rows Inserted"
,updated "Rows Updated" /* ,success_code "succ" ,exit_code "exit" */
FROM
update_log
WHERE start_datetime >= '5/29/2009 16:15'
ORDER BY start_datetime DESC