Trong khi thảo luận về một giải pháp CTE đệ quy cho câu hỏi này:
@ypercube tình cờ phát hiện ra một ngoại lệ đáng ngạc nhiên, điều này dẫn chúng tôi điều tra việc xử lý các công cụ sửa đổi loại. Chúng tôi tìm thấy hành vi đáng ngạc nhiên.
1. Kiểu cast giữ lại bộ sửa đổi kiểu trong một số ngữ cảnh
Ngay cả khi được hướng dẫn không. Ví dụ cơ bản nhất:
SELECT 'vc8'::varchar(8)::varchar
Người ta có thể mong đợi varchar
(không có sửa đổi), ít nhất là tôi sẽ làm. Nhưng kết quả là varchar(8)
(có sửa đổi). Nhiều trường hợp liên quan trong fiddle dưới đây.
2. Nối mảng mất bộ sửa đổi kiểu trong một số ngữ cảnh
Không có nhu cầu, vì vậy lỗi này ở phía đối diện:
SELECT ARRAY['vc8']::varchar(8)[]
, ARRAY['vc8']::varchar(8)[] || 'vc8'::varchar(8)
Biểu thức 1 mang lại varchar(8)[]
như mong đợi.
Nhưng thứ 2, sau khi nối một cái khác varchar(8)
được tưới xuống chỉ varchar[]
(không có sửa đổi). Hành vi tương tự từ array_append()
, ví dụ trong fiddle dưới đây.
Tất cả điều này không quan trọng trong hầu hết các bối cảnh. Postgres không bị mất dữ liệu và khi được gán cho một cột, giá trị sẽ bị ép đúng loại. Tuy nhiên , sai lầm ở các hướng ngược lại lên đến đỉnh điểm trong một ngoại lệ đáng ngạc nhiên:
3. CTE đệ quy yêu cầu các loại dữ liệu khớp chính xác
Cho bảng đơn giản hóa này:
CREATE TABLE a (
vc8 varchar(8) -- with modifier
, vc varchar -- without
);
INSERT INTO a VALUES ('a', 'a'), ('bb', 'bb');
Trong khi rCTE này hoạt động cho varchar
cột vc
, nó không thành công cho varchar(8)
cột vc8
:
WITH RECURSIVE cte AS (
(
SELECT ARRAY[vc8] AS arr -- produces varchar(8)[]
FROM a
ORDER BY vc8
LIMIT 1
)
UNION ALL
(
SELECT a.vc8 || c.arr -- produces varchar[] !!
FROM cte c
JOIN a ON a.vc8 > c.arr[1]
ORDER BY vc8
LIMIT 1
)
)
TABLE cte;
LRI: truy vấn đệ quy "cte" cột 1 có ký tự loại thay đổi (8) [] trong thuật ngữ không đệ quy nhưng ký tự loại thay đổi [] tổng thể Gợi ý: Truyền đầu ra của thuật ngữ không đệ quy cho đúng loại. Vị trí: 103
Một cách giải quyết nhanh chóng sẽ được đúc text
.
Một UNION
truy vấn đơn giản không thể hiện cùng một vấn đề: nó giải quyết loại không có sửa đổi, được đảm bảo để lưu giữ tất cả thông tin. Nhưng rCTE thì kén chọn hơn.
Ngoài ra, bạn sẽ không gặp phải vấn đề với việc được sử dụng phổ biến hơn max(vc8)
thay vì ORDER BY
/ LIMIT 1
, bởi vì max()
và bạn bè giải quyết text
ngay lập tức (hoặc loại cơ sở tương ứng mà không cần sửa đổi).
SQL Fiddle trình bày 3 điều:
- Một loạt các biểu thức ví dụ bao gồm kết quả đáng ngạc nhiên.
- Một rCTE đơn giản hoạt động với
varchar
(không có sửa đổi). - Cùng rCTE đưa ra một ngoại lệ cho
varchar(n)
(với công cụ sửa đổi).
Fiddle là cho pg 9.3. Tôi nhận được kết quả tương tự tại địa phương cho pg 9.4.4.
Tôi đã tạo các bảng từ các biểu thức demo để có thể hiển thị loại dữ liệu chính xác bao gồm cả công cụ sửa đổi. Mặc dù pgAdmin hiển thị thông tin này ngoài hộp nhưng nó không có sẵn từ sqlfiddle. Đáng chú ý, nó cũng không có sẵn trong psql
(!). Điều này được biết là thiếu sót trong psql và một giải pháp khả thi đã được thảo luận trên các tin tặc pssql trước đây - nhưng chưa được thực hiện. Đây có thể là một trong những lý do vấn đề chưa được phát hiện và khắc phục.
Ở cấp độ SQL, bạn có thể sử dụng pg_typeof()
để lấy loại (nhưng không phải là công cụ sửa đổi).
Câu hỏi
Cùng nhau, 3 vấn đề làm cho một mớ hỗn độn.
Nói chính xác, vấn đề 1. không liên quan trực tiếp, nhưng nó phá hỏng cách khắc phục dường như rõ ràng với một diễn viên trong thuật ngữ không đệ quy: ARRAY[vc8]::varchar[]
hoặc tương tự, làm tăng thêm sự nhầm lẫn.
Những mặt hàng nào trong số này là một lỗi, trục trặc hoặc chỉ là nó phải như thế nào?
Tôi đang thiếu một cái gì đó hay chúng ta nên báo cáo một lỗi?
UNION
các truy vấn đơn giản . Có thể là chúng tôi đã tìm thấy ba lỗi nhỏ độc lập cùng một lúc? (Sau nhiều tháng và nhiều tháng không tìm thấy như vậy.) Bạn sẽ cảm thấy lỗi nào trong số đó?