ý tưởng chung
Có hai phương pháp chính để tạo ra dữ liệu trong MySQL. Một là tạo ra dữ liệu khi đang chạy truy vấn và một cái khác là để có nó trong cơ sở dữ liệu và sử dụng nó khi cần thiết. Tất nhiên, thứ hai sẽ nhanh hơn lần đầu tiên nếu bạn định chạy truy vấn của bạn thường xuyên. Tuy nhiên, thứ hai sẽ yêu cầu một bảng trong cơ sở dữ liệu mà chỉ có mục đích sẽ là để tạo ra các dữ liệu bị thiếu. Nó cũng sẽ yêu cầu bạn có đủ đặc quyền để tạo bảng đó.
động sinh dữ liệu
Cách tiếp cận này liên quan đến việc làm UNION
s để tạo ra một bảng giả có thể được sử dụng để tham gia bảng thực tế với. Truy vấn khủng khiếp và lặp đi lặp lại là:
select aDate from (
select @maxDate - interval (a.a+(10*b.a)+(100*c.a)+(1000*d.a)) day aDate from
(select 0 as a union all select 1 union all select 2 union all select 3
union all select 4 union all select 5 union all select 6 union all
select 7 union all select 8 union all select 9) a, /*10 day range*/
(select 0 as a union all select 1 union all select 2 union all select 3
union all select 4 union all select 5 union all select 6 union all
select 7 union all select 8 union all select 9) b, /*100 day range*/
(select 0 as a union all select 1 union all select 2 union all select 3
union all select 4 union all select 5 union all select 6 union all
select 7 union all select 8 union all select 9) c, /*1000 day range*/
(select 0 as a union all select 1 union all select 2 union all select 3
union all select 4 union all select 5 union all select 6 union all
select 7 union all select 8 union all select 9) d, /*10000 day range*/
(select @minDate := '2001-01-01', @maxDate := '2002-02-02') e
) f
where aDate between @minDate and @maxDate
Dù sao, nó đơn giản hơn vẻ bề ngoài. Nó làm cho các sản phẩm Descartes của các bảng có nguồn gốc với 10
giá trị số do đó kết quả sẽ có 10^X
hàng trong đó X
là số lượng các bảng có nguồn gốc trong truy vấn. Trong ví dụ này, có 10000
phạm vi ngày để bạn có thể thể hiện thời gian trên 27
năm. Nếu bạn cần thêm, hãy thêm một số khác UNION
vào truy vấn và cập nhật khoảng thời gian và nếu bạn không cần quá nhiều, bạn có thể xóa UNION
s hoặc giá trị riêng lẻ khỏi các bảng có nguồn gốc. Chỉ cần làm rõ, bạn có thể tinh chỉnh khoảng thời gian bằng cách áp dụng bộ lọc với mệnh đề WHERE
trên các biến số @minDate
và @maxDate
(nhưng không sử dụng khoảng thời gian dài hơn biến mà bạn đã tạo bằng sản phẩm Descartes).
tĩnh thế hệ dữ liệu
Giải pháp này sẽ yêu cầu bạn tạo một bảng trong cơ sở dữ liệu của bạn. Cách tiếp cận này tương tự như cách tiếp cận trước. Trước tiên, bạn sẽ phải chèn dữ liệu vào bảng đó: một dải các số nguyên khác nhau từ 1
đến X
trong đó X
là phạm vi cần thiết tối đa.Một lần nữa, nếu bạn không chắc chắn chỉ cần chèn 100000
giá trị và bạn sẽ có thể tạo phạm vi ngày trong hơn 273
năm. Vì vậy, một khi bạn đã có những dãy số nguyên, bạn có thể biến nó thành một phạm vi ngày như thế này:
select '2012-01-01' + interval value - 1 day aDay from seq
having aDay <= '2012-01-05'
Giả sử một bảng tên seq
với một cột tên là value
. Ở trên cùng ngày từ ngày và ở dưới cùng ngày đến.
Biến này vào một cái gì đó hữu ích
Ok, bây giờ chúng tôi có thời gian cập nhật của chúng tôi được tạo ra nhưng chúng tôi vẫn mất tích một cách để truy vấn dữ liệu và hiển thị các giá trị bị mất như một thực tế 0
. Đây là nơi mà left join
đến để giải cứu. Để đảm bảo chúng ta đều trên cùng một trang, left join
tương tự như một inner join
nhưng chỉ có một khác biệt: nó sẽ lưu giữ tất cả các bản ghi từ bảng bên trái của kết nối, bất kể có bản ghi trùng khớp trên bảng bên phải. Nói cách khác, inner join
sẽ xóa tất cả các hàng không phù hợp khi tham gia trong khi left join
sẽ giữ các hàng trên bảng bên trái và, đối với các bản ghi bên trái không có bản ghi khớp trên bảng bên phải, left join
sẽ điền vào "không gian" với giá trị null
. Vì vậy, chúng ta nên tham gia bảng miền của chúng tôi (bảng có dữ liệu "bị thiếu") với bảng mới được tạo của chúng tôi đưa bảng thứ hai vào phần bên trái của liên kết và phần trước ở bên phải, để tất cả các yếu tố được xem xét, bất kể sự hiện diện của chúng trong bảng tên miền.
Ví dụ, nếu chúng ta có một bảng domainTable
với các lĩnh vực ID, birthDate
và chúng tôi muốn thấy một số lượng của tất cả các birthDate
trong 5
ngày đầu tiên của 2012
mỗi ngày và nếu tính là 0
để hiển thị giá trị đó, thì đây truy vấn có thể được chạy:
select allDays.aDay, count(dt.id) from (
select '2012-01-01' + interval value - 1 day aDay from seq
having aDay <= '2012-01-05'
) allDays
left join domainTable dt on allDays.aDay = dt.birthDate
group by allDays.aDay
này tạo ra một bảng có nguồn gốc với tất cả các ngày requried (chú ý tôi đang sử dụng thế hệ dữ liệu tĩnh) và thực hiện một left join
chống lại bảng miền của chúng tôi, vì vậy tất cả các ngày sẽ được hiển thị, không phân biệt về việc liệu họ có một giá trị phù hợp trong bảng miền của chúng tôi hay không. Cũng lưu ý rằng count
nên được thực hiện trên trường có giá trị null
vì những giá trị này không được tính.
Thuyết minh được coi
1) Các truy vấn có thể được sử dụng để truy vấn các khoảng khác (tháng, năm) thực hiện những thay đổi nhỏ đối với mã
2) Thay vì thể xác định rõ số ngày bạn có thể truy vấn cho min
và max
giá trị từ các bảng miền như thế này:
select (select min(aDate) from domainTable) + interval value - 1 day aDay
from seq
having aDay <= (select max(aDate) from domainTable)
Điều này sẽ tránh tạo nhiều hồ sơ hơn mức cần thiết.
Trên thực tế trả lời câu hỏi của bạn
Tôi nghĩ bạn nên đã tìm ra cách để làm những gì bạn muốn. Dù sao, đây là các bước để những người khác có thể hưởng lợi từ họ quá.Đầu tiên, tạo bảng số nguyên . Thứ hai, hãy chạy truy vấn này:
select allDays.aDay, count(mt.id) aCount from (
select (select date(min(created_at)) from my_table) + interval value - 1 day aDay
from seq s
having aDay <= (select date(max(created_at)) from my_table)
) allDays
left join my_table mt on allDays.aDay = date(mt.created_at)
group by allDays.aDay
Tôi đoán created_at
là ngày giờ và đó là lý do bạn kết nối theo cách đó. Tuy nhiên, điều đó xảy ra là cách MySQL tự nhiên lưu trữ ngày, vì vậy tôi chỉ nhóm theo trường ngày nhưng hãy đúc created_at
thành kiểu dữ liệu thực tế date
. Bạn có thể chơi với nó bằng cách sử dụng này fiddle.
Và đây là các dữ liệu giải pháp tạo động:
select allDays.aDay, count(mt.id) aCount from (
select @maxDate - interval a.a day aDay from
(select 0 as a union all select 1 union all select 2 union all select 3
union all select 4 union all select 5 union all select 6 union all
select 7 union all select 8 union all select 9) a, /*10 day range*/
(select @minDate := (select date(min(created_at)) from my_table),
@maxDate := (select date(max(created_at)) from my_table)) e
where @maxDate - interval a.a day between @minDate and @maxDate
) allDays
left join my_table mt on allDays.aDay = date(mt.created_at)
group by allDays.aDay
Như bạn có thể nhìn thấy bộ xương của truy vấn là giống như trước đó. Điều duy nhất thay đổi là cách bảng bắt nguồn allDays
được tạo ra. Bây giờ, cách mà bảng dẫn xuất được tạo ra cũng hơi khác so với cái mà tôi đã thêm trước đây. Điều này là do trong danh sách ví dụ, tôi chỉ cần một phạm vi ngày 10
. Như bạn có thể thấy, nó dễ đọc hơn là thêm một phạm vi ngày 1000
. Đây là fiddle cho giải pháp động để bạn có thể chơi với nó.
Hy vọng điều này sẽ hữu ích!
Đây thực sự là lựa chọn duy nhất của bạn trừ khi bạn có thể tạo những mục bị thiếu trong mã của bạn sau khi sele cting hồ sơ bạn có. Lưu ý rằng mặc dù bạn sẽ phải giữ bảng calendar_date này chứa đầy các ngày và hy vọng rằng bạn không quên thêm nhiều hơn bạn hiện đang cần. (Bạn sẽ đi bao nhiêu năm trong tương lai?) Cá nhân tôi không thích ý tưởng này vì nó cũng hạn chế bạn nhóm lại theo khoảng thời gian bạn đã chọn. Điều gì xảy ra nếu ngày mai bạn muốn hiển thị những thứ được nhóm theo Giờ? – Vyrotek
Để rõ ràng, trên thực tế, không có giải pháp tốt cho vấn đề này khi sử dụng SQL. –
Các tệp lịch rất hữu ích cho một loạt các sự kiện (đặc biệt là trong các tình huống bán lẻ, nơi mà lịch tài chính không phải lúc nào cũng lập bản đồ cho một bộ tài chính), bao gồm vấn đề cụ thể này. Bạn có thể tạo các câu lệnh ảo trong câu lệnh ... với CTE đệ quy (không có trong mySQL). –