关于我的数据库日期数据类型讨论:有效吗?值得吗?还有其他人感觉到吗?


13

我花了很多时间在SO上回答SQL问题。我经常遇到这样的问题:

SELECT * FROM person WHERE birthdate BETWEEN '01/01/2017' AND '01/03/2017'

SELECT * FROM person WHERE birthdate BETWEEN '2017-01-01' AND '2017-03-01'

SELECT * FROM person WHERE birthdate BETWEEN 'some string' AND 'other string'

即要么依赖给定参数从字符串到日期的隐式转换(错误),要么依赖数据库将x百万个数据库行值转换为字符串并进行字符串比较(更糟糕)

我偶尔会发表评论,尤其是如果这是一个非常有名的用户,他们编写了一个明智的答案,但是我认为,他们的数据类型确实应该少一些草率/字符串式输入

注释通常采用以下形式:如果他们使用to_date(Oracle),str_to_date(MySQL),convert(SQLSERVER)或类似的机制将字符串显式转换为日期,可能会更好:

    --oracle
    SELECT * FROM person WHERE birthdate BETWEEN TO_DATE('20170101', 'YYYYMMDD') AND TO_DATE('20170301', 'YYYYMMDD')

    --mysql
    SELECT * FROM person WHERE birthdate BETWEEN STR_TO_DATE('20170101', '%Y%m%d') AND STR_TO_DATE('20170301', '%Y%m%d')

    --SQLS, ugh; magic numbers
    SELECT * FROM person WHERE birthdate BETWEEN CONVERT(datetime, '20170101', 112) AND CONVERT(datetime, '20170301', 112)

我这样做的技术理由是,它对日期的格式是明确的,并确保少数几个源参数确实成为目标列的数据类型。这样可以避免数据库错误地进行隐式转换(第一个示例的3rd Jan / 1st Mar参数),并且可以防止db决定将表中的一百万个日期值转换为字符串(使用某些服务器特定的日期)格式可能甚至与sql中的字符串参数中的日期格式都不匹配)以进行比较-恐怖比比皆是

我这样做的社会/学术理由是SO是一个学习网站;人们从中隐性或显性地获取知识。要使用此查询作为答案来打新手:

SELECT * FROM person WHERE birthdate BETWEEN '2017-01-01' AND '2017-03-01'

可能会导致他们认为这很明智,并根据他们喜欢的某种格式调整日期:

SELECT * FROM person WHERE birthdate BETWEEN '01/01/2017' AND '01/03/2017'

如果他们至少看到了一些明确的尝试来转换日期,他们可能会以其怪异的日期格式开始执行此操作,并在它们出现之前将其永久杀死。毕竟,我们(I)试图阻止人们养成SQL注入习惯(@pBirthdate当前端具有datetime类型时,有人会提倡参数化查询,然后向驱动程序声明字符串吗?)

回到我提出建议后会发生的情况:我通常会对“明确使用x”建议有所反驳,例如“其他人都这样做”,“它总是对我有用”,“向我展示一些手册或参考文档”那说我应该是露骨的”甚至“是什么?”

针对这些问题,我问他们是否WHERE age = '99'通过将年龄作为字符串传递来搜索int列。回答是:“别傻,我们在搜索int时不需要放',因此他们对某些数据类型的想法有所了解,但也许与搜索int的逻辑飞跃无关通过传递字符串(显然是愚蠢的)并通过传递字符串(显然是明智的)来搜索日期列是虚伪的

因此,在我们的SQL中,我们有一种方法可以将内容写为数字(使用数字,不带分隔符),将内容写为字符串(在撇号分隔符之间使用任何东西)。为什么没有日期分隔符?在大多数数据库中,这是基本数据类型吗?是否可以通过以与javascript让我们通过在/某些字符的任意一侧指定正则表达式相同的方式编写日期来解决整个问题。/Hello\s+world/。为什么没有约会用的东西?

实际上,据我所知,(仅)Microsoft Access实际上具有指示“在这些定界符之间已写有日期的符号”,因此我们可以得到一个很好的快捷方式,例如WHERE datecolumn = #somedate#,日期显示仍然容易出现问题,例如mm / di vs dd / mm,因为MS总是在VB人群认为是个好主意的东西上玩得很快而松散


回到主要要点:我认为使用这种媒介是明确的,这迫使我们将许多不同的数据类型作为字符串进行传递是明智的。

这是有效的断言吗?

我应该继续进行这次讨伐吗?字符串输入是现代的禁忌法是否有效?还是在推销查询时WHERE datecolumn = 'string value'绝对肯定地将每个RDBMS(包括古代版本)正确地将字符串转换为日期,并且在不转换表数据/不使用索引的情况下进行搜索?我怀疑不是这样,至少从Oracle 9的个人经验来看,我也怀疑如果字符串始终以某种ISO标准格式编写,并且该列具有某种日期风味,那么可能会有一些逃脱的情况。字符串参数将始终正确地隐式转换。这样对吗?

这是一项值得的任务吗?

许多人似乎不了解,不在乎或表现出一些虚伪,因为他们的整数是整数,但日期是字符串。.尽管大多数人的共同之处是很少有人转过身说:“你知道什么,我同意你的观点。从现在开始,我将明确说明我的约会。”


我什至看到有人在WHERE datecolumn = 01/02 / 12'`上遇到问题,有可能他们要求的年份是1912、2012、2001、1901、12或1。这也是数据库世界之外的一个问题,数字无法理解为什么转换"09"为int会导致崩溃的程序员中,有9个不是有效的八进制数字,而前导0使字符串在许多系统中都是八进制的
Steve Barnes

2
我确实想延长我的例子来询问是否WHERE age = '0x0F'是希望数据库将搜索十五岁的一个有效方式..
凯斯Jard

1
我删除了一个题外话的问题-我们不进行资源请求。出于这个原因,给出了2个近票之一。否则,我认为这是一个有效的问题,尽管它可能太宽泛了。我希望消除离题的问题有助于缩小范围。
Thomas Owens

TL; DR,但在生产系统中,我希望这样的日期几乎总是存在于参数中。与是否使用隐式转换相比,对查询中的日期进行硬编码是一个更大的问题。如果我正在写一些扔掉的查询,它要么起作用,要么不起作用。无论如何,我永远不会这样做(因为我永远都不会记住默认的日期格式),但是我不确定这有多重要。
JimmyJames

1
生活就是在战斗。在我看来,这只是不值得战斗...
罗比·迪

Answers:


7

你写了:

是1月1日至1月3日或3月1日的参数。

这确实是潜在的错误来源。向其他读者指出这一点可能会对其他读者有所帮助,所以是的,这是一个有效的问题。但是,为了具有建设性,我会

  • 请参考ANSI SQL并使用该标准中的DATE或DATETIME文字

  • 使用特定DBMS的通常,明确的日期时间格式(并注明使用哪种SQL方言)

不幸的是,并不是每个DBMS都以完全相似的方式支持ANSI SQL日期文字(如果它们完全支持的话),因此通常会导致第二种方法的变体。不同的数据库供应商未严格实施“标准”这一事实,这可能是问题所在。

还要注意,对于许多现实世界系统,即使客户端应用程序已本地化,人们实际上仍可以依赖数据库服务器上特定的固定语言环境,因为只有一种服务器,始终以相同的方式进行配置。因此,对于在其上使用的特定系统上使用的任何SQL,通常可以将'01 / 03/2017'的格式固定为'dd / mm / yyyy'或'mm / dd / yyyy'。因此,如果有人告诉您“它总是对我有用”,那么这对于他的环境可能确实是一个明智的答案。如果是这种情况,那么讨论该主题就变得不那么值得了。

谈论“性能原因”:只要不存在可衡量的性能问题,就与“潜在性能问题”争论是相当迷信的。如果时间差仅为1/1000秒,则数据库执行一百万个字符串到日期的转换是否无关紧要,而真正的瓶颈是导致查询持续10秒的网络。因此,只要有人明确要求性能方面的考虑,最好不要考虑这些问题。

我应该继续进行这次讨伐吗?

我告诉你一个秘密:我讨厌宗教战争。它们不会导致任何有用的东西。因此,如果SQL中的日期/时间规范含糊不清可能会导致问题,请提及它们,但是如果在当前环境中并没有真正给他们带来任何好处,请不要试图迫使人们变得更加僵化。


不过,这与美国人与明智的日期格式的歧义无关。它是关于在SQL语句中将日期作为字符串传递,并依赖于对日期的隐式转换是否明智。数据库必须为所有百万行执行一百万个date-> str转换的问题是一个性能方面,一个查询可能只需要1/1000秒的时间,但是现在可以想象在这样的背景下进行并发用户。更大的性能问题是转换数据意味着索引将不再使用,而且可能非常严重
Caius Jard

@CaiusJard:我的回答是正确的:有时是明智的,有时不是,这取决于上下文。而且说实话,我拒绝“......想像......”任何东西在这里。在性能方面,讨论任何假想的案例都是没有用的。当存在可衡量的性能问题时,是时候进行优化,有时甚至是微优化了,而不是事先进行。
布朗

有趣的是,您将其视为假设。我看到依靠隐式行为是产生错误和性能复杂性的明显机会(出于充分的文献记载的原因:如果在搜索之前对整个列数据进行了转换,索引将不起作用),并且使用明确的指示,这些将不会发生
Caius Jard

@ CaiusJard:不要玩弄单词-用“假设的”我不是指“不太可能”,我用这个词来表示任何一种想像的场景,而不是可以衡量发生情况的“实际存在的情况”。
布朗

1
@CaiusJard:如果您想打动其他行业专业人士,您应该确切地知道为什么“性能优化”与“安全优化”有很大的不同,这正是我的意思-性能问题可以在发生后进行处理,这很少太晚了。安全问题不是,应该在发生之前彻底避免它们。因此,请勿将苹果与橙子进行比较。如果您喜欢十字军东征,那么安全性论点更适合于此;-)
Doc Brown

5

您的十字军东征无法解决问题。

有两个单独的问题:

  • SQL中的隐式类型转换

  • 日期格式不明确,例如05/06/07

我了解到您是从哪里来的,但是我认为显式转换实际上并不能解决当前的问题:

  • 如果比较中的类型之间不匹配,则仍然会发生隐式转换。如果将字符串与日期进行比较,SQL将尝试首先将字符串转换为日期。因此,将日期类型的列与显式转换的日期值进行比较与将字符串格式的日期进行比较完全相同。我所看到的唯一区别是,如果将日期值与实际上不包含日期但包含字符串的列进行比较-但这在任何情况下都是错误的。

  • 使用显式转换不能解决非ISO日期格式的歧义。

我看到的唯一解决方案:

  • 不要将字符串类型的列与非字符串值进行比较。
  • 仅使用ISO类型的日期格式。

当然,永远不要将日期存储在字符串类型的列中。但是同样,日期文字的显式转换不会阻止这种情况。

可以说,隐式转换在SQL中是一个错误,但是考虑到语言的设计方式,我看不到显式转换的好处。无论如何,它都不会避免隐式转换,只会使代码更难读写。


真正。也许我应该从这个角度指出这一点,最明智的做法是确保datecolumn操作数和值操作数具有相同的数据类型(字符串,日期等)。我确实仅在以下情况下才提出此建议:我知道表列为DATETIME并且其示例答案使用的是带隐式转换的字符串操作数..
Caius Jard

在这个答案上,有些事情与我不对。您提出了一些有趣的观点,但是我觉得结论很理想。从设计的角度来看,是的,非ISO日期格式在人眼中是模棱两可的,但是如果使用显式转换,则从语法上讲,它对于解析器而言并不是模棱两可的。同样,许多涉及日期的ETL流程都需要将字符串与数据库的日期格式进行某种比较(以文件导入的形式)。试图消除字符串至今的比较对我来说似乎是不现实的。
DanK

@DanK:ETL是另一个问题-如果您要从CSV文件或其他内容读取数据,则显然必须将数据作为字符串处理,并显式解析为类型化的值。但这不是OP所描述的场景。
JacquesB '17

不过,这很容易成为我要描述的重点。储存在csv中的一串数字没有什么特别的,它要求在解析时显式声明格式,并且如果新手在SO中读取了一些答案,则它与我正在讲的参数相关,而专业人士没有做出任何努力来显式地声明日期格式,导致新手认为他们不需要担心它(或者数据库将一直正确地解析它)
Caius Jard

@ CaiusJard:我相信这些是非常不同的场景。在正常情况下谈论SQL时,我假设列具有适当的类型-即,整数列是整数类型,日期列是数据类型,依此类推。如果您在表中没有正确的类型(即,将日期存储为字符串),则会遇到麻烦,并且在查询中显式转换日期文字不会为您省钱,这就是我的观点。
雅克·

3

首先,您确实有一点。日期不应该放在字符串中。数据库引擎是复杂的野兽,您永远不会百分百确定在给定任意查询的情况下究竟会发生什么。转换为日期可以使事情变得明确,并可以提高性能。

对于大多数人来说,这不是一个值得多花心思解决的问题。如果在查询中易于使用日期文字,则很容易捍卫自己的位置。但事实并非如此。我主要使用SQL Server,因此想记住那种转换日期的麻烦并没有发生。

对于大多数人来说,性能提升是微不足道的。“为什么是老板先生,我确实花了额外的10分钟来修复这个简单的错误(我必须在Google上搜索日期转换方法,因为该语法非常特殊。)但是我节省了额外的0.00001秒很少执行的查询。” 那不会飞到我工作过的大多数地方。

但这消除了您所说的日期格式的歧义。再次,对于很多应用程序(公司内部应用程序,地方政府资料等),这并不是真正的问题。对于那些需要关注的应用程序(大型,国际或企业应用程序),要么成为UI /业务层关注的问题,要么这些公司已经拥有一支精通DBA的团队,他们已经知道这一点。TL / DR:如果国际化是一个问题,则有人已经在考虑它,并且已经按照您的建议进行了处理(或者以其他方式缓解了该问题)。

所以现在怎么办?

如果您有这样的倾向,请继续打好仗。但是,如果大多数人不觉得这很重要而不必担心,就不要感到惊讶。仅仅因为在某些情况下很重要,并不意味着那就是每个人的情况(而且可能并非如此)。因此,当您对某些在技术上正确且更好但并非真正相关的东西有所退缩时,不要感到惊讶。


1

我认为使用这种媒介显式地迫使我们将大量不同的数据类型作为字符串进行传递是明智的。

假设 “日期”在“ in” 字符串中传递,则为“ yes”。我完全同意您这样做是对的。

什么时候 “ 01/04/07”?
* 1月4日?
* 4月1日?
* [2001年4月7日]?

这些中的任何一个或全部可能都是正确的,具体取决于“计算机”选择如何解释它们。

如果必须使用文字来构建动态SQL,则日期格式必须定义明确,最好是与计算机无关(我在Windows Server上遇到了一个奇怪的问题,即Windows Service中基于日期的处理出错了因为操作员使用不同的日期格式首选项登录控制台!)。我个人只使用[d]格式“ yyyy-mm-dd”。

但是...

最好的解决方案是使用参数化的查询迫使数据类型转换之前 SQL介入-获得一个“约会”值转换成日期的参数强制类型转换早期(使其成为一个纯粹的编码问题,而不是一个SQL一个) 。


我同意,尽管可以通过使用参数化查询来解决相同的问题,但是可以这样做WHERE datecolumn = @dateParameter,然后在前端代码中,告诉数据库驱动程序@dateParameter类型为varchar并坚持"01/04/07"使用。我的问题的最初灵感是,我怀疑有人会告诉我,我对对参数化查询执行该操作感到疯狂,然后会在同一口气中给出一些看起来像这样的答案WHERE datecol = 'some string that looks like a date'(并希望新手应该知道这只是一个提示/将其参数化以避免出现问题)
Caius Jard
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.