列是没有时区的时间戳类型,但表达式是字符类型

Column is of type timestamp without time zone but expression is of type character

我正在尝试在我尝试在 Redshift 上实施 SCD2 时插入记录 但是出现错误。

目标 table 的 DDL 是

CREATE TABLE ditemp.ts_scd2_test (
    id INT
    ,md5 CHAR(32)
    ,record_id BIGINT IDENTITY
    ,from_timestamp TIMESTAMP
    ,to_timestamp TIMESTAMP
    ,file_id BIGINT
    ,party_id BIGINT
    )

这是插入语句:

INSERT
INTO ditemp.TS_SCD2_TEST(id, md5, from_timestamp, to_timestamp)

SELECT TS_SCD2_TEST_STAGING.id
    ,TS_SCD2_TEST_STAGING.md5
    ,from_timestamp
    ,to_timestamp
FROM (
    SELECT '20150901 16:34:02' AS from_timestamp
        ,CASE 
            WHEN last_record IS NULL
                THEN '20150901 16:34:02'
            ELSE '39991231 11:11:11.000'
            END AS to_timestamp
        ,CASE 
            WHEN rownum != 1
                AND atom.id IS NOT NULL
                THEN 1
            WHEN atom.id IS NULL
                THEN 1
            ELSE 0
            END AS transfer
        ,stage.*
    FROM (
        SELECT id
        FROM ditemp.TS_SCD2_TEST_STAGING
        WHERE file_id = 2
        GROUP BY id
        HAVING count(*) > 1
        ) AS scd2_count_ge_1
    INNER JOIN (
        SELECT row_number() OVER (
                PARTITION BY id ORDER BY record_id
                ) AS rownum
            ,stage.*
        FROM ditemp.TS_SCD2_TEST_STAGING AS stage
        WHERE file_id IN (2)
        ) AS stage
        ON (scd2_count_ge_1.id = stage.id)
    LEFT JOIN (
        SELECT max(rownum) AS last_record
            ,id
        FROM (
            SELECT row_number() OVER (
                    PARTITION BY id ORDER BY record_id
                    ) AS rownum
                ,stage.*
            FROM ditemp.TS_SCD2_TEST_STAGING AS stage
            )
        GROUP BY id
        ) AS last_record
        ON (
                stage.id = last_record.id
                AND stage.rownum = last_record.last_record
                )
    LEFT JOIN ditemp.TS_SCD2_TEST AS atom
        ON (
                stage.id = atom.id
                AND stage.md5 = atom.md5
                AND atom.to_timestamp > '20150901 16:34:02'
                )
    ) AS TS_SCD2_TEST_STAGING
WHERE transfer = 1

简而言之,我正在尝试将 20150901 16:34:02 插入 from_timestamp 并将 39991231 11:11:11.000 插入 to_timestamp

并获得

ERROR: 42804: column "from_timestamp" is of type timestamp without time zone but expression is of type character varying

任何人都可以建议如何解决这个问题吗?

Postgres 无法将 20150901 16:34:02(您的输入)识别为有效的 time/date 格式,因此它假定它是一个字符串。

改用标准日期格式,最好是 ISO-8601。 2015-09-01T16:34:02

SQLFiddle example

以防万一有人在这里试图从 groovy 中的变量或 Java 中的变量插入 postgresql timestamptimestampz 准备好的语句并得到相同的错误(和我一样),我设法通过将 属性 stringtype 设置为 "unspecified" 来做到这一点。根据 documentation:

Specify the type to use when binding PreparedStatement parameters set via setString(). If stringtype is set to VARCHAR (the default), such parameters will be sent to the server as varchar parameters. If stringtype is set to unspecified, parameters will be sent to the server as untyped values, and the server will attempt to infer an appropriate type. This is useful if you have an existing application that uses setString() to set parameters that are actually some other type, such as integers, and you are unable to change the application to use an appropriate method such as setInt().

Properties props = [user : "user", password: "password", 
driver:"org.postgresql.Driver", stringtype:"unspecified"]
def sql = Sql.newInstance("url", props)

有了这个 属性 集,您可以插入一个时间戳作为字符串变量,而不会出现问题标题中出现的错误。例如:

String myTimestamp= Instant.now().toString()
sql.execute("""INSERT INTO MyTable (MyTimestamp) VALUES (?)""",
[myTimestamp.toString()]

这样,postgresql 就可以正确推断出时间戳的类型(来自字符串)。希望对您有所帮助。

apache 内部-tomcat-9.0.7/conf/server.xml

将“?stringtype=unspecified”添加到 url 地址的末尾。 例如:

<GlobalNamingResources> 
<Resource name="jdbc/??" auth="Container" type="javax.sql.DataSource"
       ...
       url="jdbc:postgresql://127.0.0.1:5432/Local_DB?stringtype=unspecified"/>
</GlobalNamingResources>