Sqlserver
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Sqlserver

Tôi vẫn bị tràn Số học khi lọc theo ngày giờ truyền ngay cả khi tôi sử dụng IsDate ()

DateTime của SQL Server có miền 1753-01-01 00:00:00.000 ≤ x ≤ 9999-12-31 23:59:59,997. Năm 210 CN nằm ngoài miền đó. Do đó, vấn đề.

Nếu bạn đang sử dụng SQL Server 2008 trở lên, bạn có thể truyền nó sang DateTime2 kiểu dữ liệu và bạn sẽ là vàng (tên miền của nó là 0001-01-01 00:00:00.0000000 &le x ≤ 9999-12-31 23:59:59.9999999. Nhưng với SQL Server 2005, bạn có khá nhiều SOL.

Đây thực sự là một vấn đề của việc làm sạch dữ liệu. Xu hướng của tôi trong những trường hợp như thế này là tải dữ liệu của bên thứ 3 vào một bảng dàn với mỗi trường là các chuỗi ký tự. Sau đó, làm sạch dữ liệu tại chỗ, thay thế các ngày không hợp lệ bằng NULL. Sau khi được làm sạch, hãy thực hiện công việc chuyển đổi cần thiết để chuyển nó đến đích cuối cùng.

Một cách tiếp cận khác là sử dụng đối sánh mẫu và thực hiện lọc ngày mà không cần chuyển đổi bất kỳ thứ gì thành datetime . Giá trị ngày / giờ ISO 8601 là các chuỗi ký tự có đặc tính đáng khen ngợi là (A) con người có thể đọc được và (B) đối chiếu và so sánh đúng cách.

Những gì tôi đã làm trước đây là một số công việc phân tích để xác định tất cả các mẫu trong trường ngày giờ bằng cách thay thế các chữ số thập phân bằng 'd' và sau đó chạy nhóm group by để tính toán số lượng của mỗi mẫu khác nhau được tìm thấy. Khi bạn đã có điều đó, bạn có thể tạo một số bảng mẫu để hướng dẫn bạn. Một cái gì đó như sau:

create table #datePattern
(
  pattern varchar(64) not null primary key clustered ,
  monPos  int         not null ,
  monLen  int         not null ,
  dayPos  int         not null ,
  dayLen  int         not null ,
  yearPos int         not null ,
  yearLen int         not null ,
)

insert #datePattern values ( '[0-9]/[0-9]/[0-9] %'                          ,1,1,3,1,5,1)
insert #datePattern values ( '[0-9]/[0-9]/[0-9][0-9] %'                     ,1,1,3,1,5,2)
insert #datePattern values ( '[0-9]/[0-9]/[0-9][0-9][0-9] %'                ,1,1,3,1,5,3)
insert #datePattern values ( '[0-9]/[0-9]/[0-9][0-9][0-9][0-9] %'           ,1,1,3,1,5,4)
insert #datePattern values ( '[0-9]/[0-9][0-9]/[0-9] %'                     ,1,1,3,2,6,1)
insert #datePattern values ( '[0-9]/[0-9][0-9]/[0-9][0-9] %'                ,1,1,3,2,6,2)
insert #datePattern values ( '[0-9]/[0-9][0-9]/[0-9][0-9][0-9] %'           ,1,1,3,2,6,3)
insert #datePattern values ( '[0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9] %'      ,1,1,3,2,6,4)
insert #datePattern values ( '[0-9][0-9]/[0-9]/[0-9] %'                     ,1,2,4,1,6,1)
insert #datePattern values ( '[0-9][0-9]/[0-9]/[0-9][0-9] %'                ,1,2,4,1,6,2)
insert #datePattern values ( '[0-9][0-9]/[0-9]/[0-9][0-9][0-9] %'           ,1,2,4,1,6,3)
insert #datePattern values ( '[0-9][0-9]/[0-9]/[0-9][0-9][0-9][0-9] %'      ,1,2,4,1,6,4)
insert #datePattern values ( '[0-9][0-9]/[0-9][0-9]/[0-9] %'                ,1,2,4,2,7,1)
insert #datePattern values ( '[0-9][0-9]/[0-9][0-9]/[0-9][0-9] %'           ,1,2,4,2,7,2)
insert #datePattern values ( '[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9] %'      ,1,2,4,2,7,3)
insert #datePattern values ( '[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9] %' ,1,2,4,2,7,4)

create table #timePattern
(
  pattern varchar(64) not null primary key clustered ,
  hhPos int not null ,
  hhLen int not null ,
  mmPos int not null ,
  mmLen int not null ,
  ssPos int not null ,
  ssLen int not null ,
)
insert #timePattern values ( '[0-9]:[0-9]:[0-9]'                ,1,1,3,1,5,1 )
insert #timePattern values ( '[0-9]:[0-9]:[0-9][0-9]'           ,1,1,3,1,5,2 )
insert #timePattern values ( '[0-9]:[0-9][0-9]:[0-9]'           ,1,1,3,2,6,1 )
insert #timePattern values ( '[0-9]:[0-9][0-9]:[0-9][0-9]'      ,1,1,3,2,6,2 )
insert #timePattern values ( '[0-9][0-9]:[0-9]:[0-9]'           ,1,2,4,1,6,1 )
insert #timePattern values ( '[0-9][0-9]:[0-9]:[0-9][0-9]'      ,1,2,4,1,6,2 )
insert #timePattern values ( '[0-9][0-9]:[0-9][0-9]:[0-9]'      ,1,2,4,2,7,1 )
insert #timePattern values ( '[0-9][0-9]:[0-9][0-9]:[0-9][0-9]' ,1,2,4,2,7,2 )

Bạn có thể kết hợp hai bảng này thành 1 nhưng số lượng kết hợp có xu hướng làm bùng nổ mọi thứ, mặc dù sau đó nó đơn giản hóa truy vấn rất nhiều.

Khi bạn có điều đó, truy vấn [khá] dễ dàng, vì SQL không phải là lựa chọn ngôn ngữ tốt nhất thế giới để xử lý chuỗi:

---------------------------------------------------------------------
-- first, get your lower bound in ISO 8601 format yyyy-mm-dd hh:mm:ss
-- This will compare/collate properly
---------------------------------------------------------------------
declare @dtLowerBound varchar(255)
set @dtLowerBound = convert(varchar,dateadd(year,-1,current_timestamp),121)

-----------------------------------------------------------------
-- select rows with a start date more recent than the lower bound
-----------------------------------------------------------------
select isoDate =       + right( '0000' + substring( t.startDate , coalesce(dt.yearPos,1) , coalesce(dt.YearLen,0) ) , 4 )
                 + '-' + right(   '00' + substring( t.startDate , coalesce(dt.monPos,1)  , coalesce(dt.MonLen,0)  ) , 2 )
                 + '-' + right(   '00' + substring( t.startDate , coalesce(dt.dayPos,1)  , coalesce(dt.dayLen,0)  ) , 2 )
                 + case
                   when tm.pattern is not null then
                       ' ' + right( '00' + substring(ltrim(rtrim( substring(t.startDate,dt.YearPos+dt.YearLen,1+len(t.startDate)-(dt.YearPos+dt.YearLen) ) ) ), tm.hhPos , tm.hhLen ) , 2 )
                     + ':' + right( '00' + substring(ltrim(rtrim( substring(t.startDate,dt.YearPos+dt.YearLen,1+len(t.startDate)-(dt.YearPos+dt.YearLen) ) ) ), tm.mmPos , tm.mmLen ) , 2 )
                     + ':' + right( '00' + substring(ltrim(rtrim( substring(t.startDate,dt.YearPos+dt.YearLen,1+len(t.startDate)-(dt.YearPos+dt.YearLen) ) ) ), tm.ssPos , tm.ssLen ) , 2 )
                   else ''
                   end
,*
from someTableWithBadData t
left join #datePattern dt on t.startDate like dt.pattern
left join #timePattern tm on ltrim(rtrim( substring(t.startDate,dt.YearPos+dt.YearLen,1+len(t.startDate)-(dt.YearPos+dt.YearLen) ) ) )
                             like tm.pattern
where @lowBound <=        + right( '0000' + substring( t.startDate , coalesce(dt.yearPos,1) , coalesce(dt.YearLen,0) ) , 4 )
                 + '-' + right(   '00' + substring( t.startDate , coalesce(dt.monPos,1)  , coalesce(dt.MonLen,0)  ) , 2 )
                 + '-' + right(   '00' + substring( t.startDate , coalesce(dt.dayPos,1)  , coalesce(dt.dayLen,0)  ) , 2 )
                 + case
                   when tm.pattern is not null then
                       ' ' + right( '00' + substring(ltrim(rtrim( substring(t.startDate,dt.YearPos+dt.YearLen,1+len(t.startDate)-(dt.YearPos+dt.YearLen) ) ) ), tm.hhPos , tm.hhLen ) , 2 )
                     + ':' + right( '00' + substring(ltrim(rtrim( substring(t.startDate,dt.YearPos+dt.YearLen,1+len(t.startDate)-(dt.YearPos+dt.YearLen) ) ) ), tm.mmPos , tm.mmLen ) , 2 )
                     + ':' + right( '00' + substring(ltrim(rtrim( substring(t.startDate,dt.YearPos+dt.YearLen,1+len(t.startDate)-(dt.YearPos+dt.YearLen) ) ) ), tm.ssPos , tm.ssLen ) , 2 )
                   else ''
                   end

Như tôi đã nói, SQL không phải là sự lựa chọn tốt nhất cho việc ghép chuỗi.

Điều này sẽ giúp bạn ... 90% ở đó. Kinh nghiệm cho tôi biết rằng bạn vẫn sẽ tìm thấy nhiều ngày tồi tệ hơn:tháng nhỏ hơn 1 hoặc lớn hơn 12, ngày nhỏ hơn 1 hoặc lớn hơn 31 hoặc những ngày ngoài phạm vi cho tháng đó (không có gì giống như ngày 31 tháng 2 để làm cho máy tính kêu rên) , v.v. Các chương trình cobol cũ nói riêng, được yêu thích sử dụng một trường có tất cả các số 9 để chỉ ra dữ liệu bị thiếu, chẳng hạn (mặc dù đó là một trường hợp dễ giải quyết).

Kỹ thuật ưa thích của tôi là viết một tập lệnh perl để lọc dữ liệu và tải hàng loạt dữ liệu đó vào SQL Server, sử dụng các cơ sở BCP của perl. Đó chính xác là loại không gian perl có vấn đề được thiết kế cho.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Thay đổi mật khẩu khi đăng nhập SA trong SQL Server (Ví dụ T-SQL)

  2. Truy vấn sql để tạo ngày thanh toán hàng tháng trong một phạm vi ngày

  3. Ví dụ về truy vấn trong mối quan hệ nhiều-nhiều

  4. bảo vệ thủ tục được lưu trữ bằng cách từ chối định nghĩa chế độ xem

  5. Sử dụng SQL Server làm cơ chế khóa tài nguyên