SQL选择问题

时间:2009-06-10 15:44:07

标签: sql ms-access

我在Access 2003中使用它。请原谅我,如果这是在错误的位置,但我希望有人可以提供帮助。

我有一个包含多个记录的表,这些记录具有文本表示日期字段。日期的格式如下:“06/01/2009”我需要从表格中选择所有字段,但只有6个最旧的行属于每组的范围:

COUPONS.DocType, COUPONS.PayTo, COUPONS.ContactName, COUPONS.ContactNumber,
COUPONS.DocFooter, COUPONS.PQBName, COUPONS.LetterDate, COUPONS.RetireeFirstName,
COUPONS.RetireeLastName, COUPONS.Address1, COUPONS.Address2, COUPONS.City, 
COUPONS.State, COUPONS.ZIP, COUPONS.PQBSSN, COUPONS.EmployerCode
ordered by the COUPONS.DateDue. 

赞:仅选择日期范围为01/01/2009 - 12/01/2009的记录,其中只选择6个最旧的条目。

我对此有点怀念并且没有运气。我知道这是非常基本的,但我似乎无法使这项工作。这是我用来从表中获取日期的SQL选择。

SELECT COUPONS.DocType, COUPONS.PayTo, COUPONS.ContactName, COUPONS.ContactNumber,
COUPONS.DocFooter, COUPONS.PQBName, COUPONS.LetterDate, COUPONS.RetireeFirstName, 
COUPONS.RetireeLastName, COUPONS.Address1, COUPONS.Address2, COUPONS.City, 
COUPONS.State, COUPONS.ZIP, COUPONS.PQBSSN, COUPONS.EmployerCode, COUPONS.AmountDue, 
COUPONS.DateDue, Right([DateDue],4)+Left([DateDue],2)+Mid([datedue],4,2) AS SORTDATE
FROM COUPONS
ORDER BY COUPONS.DocType, COUPONS.PayTo, COUPONS.ContactName, COUPONS.ContactNumber, 
COUPONS.DocFooter, COUPONS.PQBName, COUPONS.LetterDate, Right([DateDue],4)+Left
([DateDue],2)+Mid([datedue],4,2);

8 个答案:

答案 0 :(得分:1)

如果您可以控制数据库但必须使用基于文本的日期,请使用ODBC规范格式存储日期:

yyyy-mm-dd               // if there's no time element
yyyy-mm-dd HH:MM:ss      // if time is needed as well

这有一些明显的优势:

  • 对于不在美国并且可能认为mm-dd-yyyy意味着dd-mm-yyyy
  • 的用户来说,这是世界友好的
  • 自然按日期排序,正常<和>运算符工作正常(这些操作正在进行文本比较,它们实际上从未将文本转换为日期)。
  • 您的业务层可能无需正确调整代码即可正确读取此格式的日期
  • 如果您的字段没有实际日期,则不会像许多已发布的建议一样生成CONVERT()错误。 (例如,如果您还处理“下周二”或“N / A”等脏值,则无法清除数据库。)

使用RIGHT(),LEFT()等转换现有日期数据是一个简单的UPDATE练习,假设您当前的日期数据格式一致。

一旦您的数据以可以更容易查询的格式存储,这是一个简单的问题:

SELECT TOP 6 * FROM mytable WHERE mydate BETWEEN startdate AND enddate ORDER BY mydate DESC

至于你的分组问题,我不能很好地理解这个问题,无法提出答案。但是,以最有效的文本格式存储日期数据将有助于对其他所有内容进行排序。

好的,我要去解决你的分组问题:

SELECT DISTINCT DueDate, DocType, PayTo, ContactName, ContactNumber, [...other fields...]
FROM coupons c1
WHERE CDate(c1.DueDate) BETWEEN '01/01/2000' AND '01/01/2009'
  /* Here's where the "grouping" happens--actually just filtering out the others */
  AND (SELECT COUNT(*) FROM coupons c2 WHERE
    CDATE(c1.DueDate) >= CDATE(c2.DueDate)
    AND c2.DocType=c1.DocType
    AND c2.ContactName=c1.ContactName
    AND c2.ContactNumber=c1.ContactNumber
    [...test the other fields...]
    ) <= 6

我不记得Jet SQL的细节足以知道这个子查询是否会起作用,但我认为它会。

答案 1 :(得分:1)

如果您确定所有行中都有日期字符串,则转换为日期值的最简单的VB是:CDate([DateDue])。但是,它会在NULL上失败。

那么您可以使用以下内容获取最旧的行:

Select Top 6 *
From myTable
ORDER BY CDate([DateDue]) ASC

答案 2 :(得分:1)

我想我理解你的问题 - 让我给你一个解决你的日期问题的解决方案 - 上面有很多解决方案。

鉴于此数据:

   PQBSSN   DATE    PQBNAME
1   1/1/2009    A
1   1/2/2009    A
1   1/3/2009    A
1   1/4/2009    Z
1   1/5/2009    Z
1   1/6/2009    Z
2   1/1/2009    B
2   1/2/2009    B
2   1/3/2009    B
2   1/4/2009    B
2   1/5/2009    B
2   1/6/2009    B
3   1/1/2009    C
3   1/2/2009    C
3   1/3/2009    C
3   1/4/2009    C
3   1/5/2009    C
3   1/6/2009    C

SELECT C1.PQBSSN, C1.PQBNAME, C3.Date
FROM [SELECT DISTINCT CA.PQBSSN, CA.PQBNAME FROM COUPONS AS CA]. AS C1, 
     [SELECT DISTINCT CB.DATE FROM COUPONS AS CB]. AS C3
WHERE C3.DATE IN 
     (SELECT TOP 2 C2.DATE FROM COUPONS AS C2 WHERE C2.PQBSSN = C1.PQBSSN ORDER BY C2.DATE);

细分:

CA select提供唯一的非日期信息行

CB选择提供表格中的所有日期

“WHERE C3.DATE”选项为您提供适用于每个匹配组的日期。如果没有用于分组行的唯一键,则需要在每个独立字段的此选择的WHERE中放置检查。

这给:

   PQBSS PQBNAME Date
    1   A   1/1/2009
    1   Z   1/1/2009
    2   B   1/1/2009
    3   C   1/1/2009
    1   A   1/2/2009
    1   Z   1/2/2009
    2   B   1/2/2009
    3   C   1/2/2009

我知道这是你桌子的简化版本,但我认为它可以实现你的目的。

答案 3 :(得分:0)

您可能正在添加像这样的日期值

Right([DateDue],4)+Left([DateDue],2)+Mid([datedue],4,2);

(例如:1996 + 04 + 21 = 2021)

请在您的订单条款中尝试此操作

Right([DateDue],4),Left([DateDue],2),Mid([datedue],4,2);

或尝试连接值而不是添加它们:

Right([DateDue],4) & Left([DateDue],2) & Mid([datedue],4,2);

(例如:1996 + 04 + 21 = 19960421)

之后确实只使用top子句。我没意识到那可能是个问题,对不起。

答案 4 :(得分:0)

我不是Access的专家,但如果您的Access版本支持,我认为您可以使用“SELEC TOP”:

SELECT TOP 6 COUPONS.DocType, COUPONS.PayTo, COUPONS.ContactName, COUPONS.ContactNumber, COUPONS.DocFooter, COUPONS.PQBName, COUPONS.LetterDate, COUPONS.RetireeFirstName, COUPONS.RetireeLastName, COUPONS.Address1, COUPONS.Address2, COUPONS.City, COUPONS.State, COUPONS.ZIP, COUPONS.PQBSSN, COUPONS.EmployerCode, COUPONS.AmountDue, COUPONS.DateDue, Right([DateDue],4)+Left([DateDue],2)+Mid([datedue],4,2) AS SORTDATE FROM COUPONS

ORDER BY Right([DateDue],4)+ Left([DateDue],2)+ Mid([datedue],4,2),COUPONS.DocType,COUPONS.PayTo,COUPONS.ContactName,COUPONS.ContactNumber, COUPONS.DocFooter,COUPONS.PQBName,COUPONS.LetterDate

答案 5 :(得分:0)

因为你的问题很难阅读(格式化家伙!!)我将讨论你最初的问题。

如果日期表示为字符,您如何选择六个最早的日期。

您需要将约会时间转换为日期时间,然后通过以下方式执行订单:

SELECT top 6 CAST(datedue as datetime) as DateDue from test order by DateDue asc

答案 6 :(得分:0)

如果我正确地阅读您的问题,您似乎有一个包含类似日期的数据的文本字段,并且您希望能够按时间顺序(在一定范围内)对其进行排序。

经过一些快速谷歌搜索后,似乎Access有一个CDate()函数,您可以使用它将字符串数据转换为日期。其次,一旦你解决了这个问题,选择日期范围的实际查询可能看起来像这样(至少在Sql Server中 - Access的语法可能类似,但可能略有不同):

SELECT TOP 6 [...]
FROM [...]
WHERE DateDue BETWEEN @BeginDate AND @EndDate
ORDER BY DateDue ASC

这是关于如何在日期范围内获得六个最旧条目的一般概念。

答案 7 :(得分:0)

回答未完成,正在努力......

重述问题:查询返回太多行,对于ORDER BY子句中列出的 seven 前六列,每个'group'的每个'group'只需要六行。< / p>

你已经解决了日期问题。与流行的观点相反,没有必要强制转换为DATETIME以使此查询起作用。无论您是在DATETIME表达式还是VARCHAR表达式上进行排序,问题都是一样的。您只需要EACH GROUP的“最低”n值。

要在单个查询中获取此结果集,我认为这将需要带有内联视图的stopkey谓词,Access是否支持公共表表达式?

例如。

WITH cte AS 
( SELECT ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...) AS ROWNUM
       , ...
    FROM COUPONS c
)
SELECT ...
  FROM cte
 WHERE cte.ROWNUM <= 6

-OR -

SELECT TOP 6 ... 
  FROM ...
 GROUP
    BY ...

回答未完成


来自OP的示例SQL语句,重新格式化为“人类可读”:

SELECT c.DocType
     , c.PayTo
     , c.ContactName
     , c.ContactNumber
     , c.DocFooter
     , c.PQBName
     , c.LetterDate
     , c.RetireeFirstName
     , c.RetireeLastName
     , c.Address1
     , c.Address2
     , c.City
     , c.State
     , c.ZIP
     , c.PQBSSN
     , c.EmployerCode
     , c.AmountDue
     , c.DateDue
     , Right(c.[DateDue],4)+Left(c.[DateDue],2)+Mid(c.[DateDue],4,2) AS SORTDATE
  FROM COUPONS c
 ORDER
    BY c.DocType
     , c.PayTo
     , c.ContactName
     , c.ContactNumber
     , c.DocFooter
     , c.PQBName
     , c.LetterDate
     , Right(c.[DateDue],4)+Left(c.[DateDue],2)+Mid(c.[DateDue],4,2)