当我试图同时更新多行时出现问题。
以下是我使用的表和查询(为了更好地阅读,将其简化):
表
CREATE TABLE foo
(
pkid integer,
x integer,
y integer
)
查询
UPDATE foo SET x=t.x, y=t.y FROM
(VALUES (50, 50, 1),
(100, 120, 2))
AS t(x, y, pkid) WHERE foo.pkid=t.pkid
这个查询工作得很好,但是当我试图执行一个所有x或y值都是null的查询时,我得到一个错误:
带空值的查询
UPDATE foo SET x=t.x, y=t.y FROM
(VALUES (null, 20, 1),
(null, 50, 2))
AS t(x, y, pkid) WHERE foo.pkid=t.pkid
错误
ERROR: column "x" is of type integer but expression is of type text
LINE 1: UPDATE foo SET x=t.x FROM
解决这个问题的唯一方法是将至少一个值(null,20,1)
更改为(null: int,50,2)
,但我不能这样做,因为我有一个生成这些更新多行查询的函数,它对列类型一无所知。
这里最好的解决方案是什么?有没有更好的多行更新查询?有没有像AS t(x: gettype(foo. x),y: gettype(foo. y),pchild: gettype(foo.pkid))
这样的函数或语法?
脚本将从foo创建一个临时表。它将具有与foo相同的数据类型。使用不可能的条件,使其为空:
select x, y, pkid
into temp t
from foo
where pkid = -1
将脚本插入其中:
insert into t (x, y, pkid) values
(null, 20, 1),
(null, 50, 2)
现在更新一下:
update foo
set x=t.x, y=t.y
from t
where foo.pkid=t.pkid
最后放下它:
drop table t
如果有一个生成查询的脚本,可以提取并缓存每列的数据类型,并相应地创建类型转换。例如:
SELECT column_name,data_type,udt_name
FROM information_schema.columns
WHERE table_name = 'foo';
从这个udt_的名字中,你将得到你在最后一段中解释的必要演员阵容。此外,您还可以这样做:
UPDATE foo
SET x = t.x
FROM (VALUES(null::int4,756),(null::int4,6300))
AS t(x,pkid)
WHERE foo.pkid = t.pkid;
对于独立的VALUES
表达式,PostgreSQL不知道数据类型应该是什么。对于简单的数字文字,系统很乐意假设匹配的类型。但是对于其他输入(如NULL
),您需要显式强制转换——正如您已经发现的那样。
您可以查询pg_catalog
(快速,但特定于PostgreSQL)或information_schema
(缓慢,但标准的SQL)来找出并准备具有适当类型的语句。
或者你可以使用这些简单的“技巧”(我把最好的留到最后):
UPDATE foo f
SET x = t.x
, y = t.y
FROM (
(SELECT pkid, x, y FROM foo LIMIT 0) -- parenthesis needed with LIMIT
UNION ALL
VALUES
(1, 20, NULL) -- no type casts here
, (2, 50, NULL)
) t -- column names and types are already defined
WHERE f.pkid = t.pkid;
子查询的第一个子选择:
(SELECT x, y, pkid FROM foo LIMIT 0)
获取列的名称和类型,但是LIMIT 0
阻止它添加实际的行。后续行被强制为现在定义良好的行类型,并立即检查它们是否与类型匹配。这应该是对原始形式的一个微妙的额外改进。
在为表的所有列提供值的同时,第一行可以使用这种简短的语法:
(TABLE foo LIMIT 0)
主要限制:Postgres将独立的值
表达式的输入文本立即转换为“尽力”类型。当它稍后尝试强制转换到第一个选择的给定类型时,如果假定类型和目标类型之间没有已注册的赋值强制转换,则对于某些类型来说可能已经太晚了。示例:
text
-
赞成的意见:
最低开销
骗局:
对于某些类型,类型解析可能会失败。
UPDATE foo f
SET x = t.x
, y = t.y
FROM (
(SELECT pkid, x, y FROM foo LIMIT 0) -- parenthesis needed with LIMIT
UNION ALL SELECT 1, 20, NULL
UNION ALL SELECT 2, 50, NULL
) t -- column names and types are already defined
WHERE f.pkid = t.pkid;
赞成的意见:
比如0。,但避免了失败的类型解析
骗局:
UNION ALL SELECT
对于较长的行列表,比VALUES
表达式慢,正如您在测试中发现的那样。
- 每行的详细语法。
...
FROM (
VALUES
((SELECT pkid FROM foo LIMIT 0)
, (SELECT x FROM foo LIMIT 0)
, (SELECT y FROM foo LIMIT 0)) -- get type for each col individually
, (1, 20, NULL)
, (2, 50, NULL)
) t (pkid, x, y) -- columns names not defined yet, only types.
...
与0相反。这避免了过早的类型解析。
VALUES
表达式中的第一行是一行NULL
值,它定义了所有后续行的类型。这个领先的噪声行稍后会被过滤,其中f.pkid=t.pkid
,因此它永远看不到曙光。出于其他目的,可以在子查询中使用偏移量1消除添加的第一行。
赞成的意见:
通常快于1.(甚至0.)
- 具有许多列且只有少数列的表的简短语法。
- 您只需要知道表的相关列名。
骗局:
仅适用于少数行的详细语法
UPDATE foo f
SET x = (t.r).x -- parenthesis needed to make syntax unambiguous
, y = (t.r).y
FROM (
VALUES
('(1,20,)'::foo) -- columns need to be in default order of table
,('(2,50,)') -- nothing after the last comma for NULL
) t (r) -- column name for row type
WHERE f.pkid = (t.r).pkid;
您显然知道表名。如果您还知道列数及其顺序,您可以使用它。
对于PostgreSQL中的每个表,都会自动注册一个行类型。如果匹配表达式中的列数,则可以强制转换为表的行类型(
'(1,50,)::foo
),从而隐式指定列类型。在逗号后面不加任何内容以输入NULL
值。为每个不相关的尾随列添加逗号
在下一步中,您可以使用演示的语法访问各个列。有关字段选择的更多信息,请参见手册。
或者,您可以添加一行NULL值,并对实际数据使用统一语法:
sql prettyprint-override">...
VALUES
((NULL::foo)) -- row of NULL values
, ('(1,20,)') -- uniform ROW value syntax for all
, ('(2,50,)')
...
赞成的意见:
最快(至少在我的测试中,行和列很少)
骗局:
从记录/行/复合类型中选择字段的语法并不广为人知
比如3。,但对于标准语法中的分解行:
UPDATE foo f
SET x = t.x
, y = t.y
FROM (
VALUES
(('(1,20,)'::foo).*) -- decomposed row of values
, (2, 50, NULL)
) t(pkid, x, y) -- arbitrary column names (I made them match)
WHERE f.pkid = t.pkid; -- eliminates 1st row with NULL values
或者,再次使用空值的前导行:
...
VALUES
((NULL::foo).*) -- row of NULL values
, (1, 20, NULL) -- uniform syntax for all
, (2, 50, NULL)
...
利与弊,如3。,但使用更常见的语法
而且你需要拼出列名(如果你需要的话)。
就像Unril评论的那样,我们可以结合2的优点。和4。要仅提供列的子集,请执行以下操作:
UPDATE foo f
SET ( x, y)
= (t.x, t.y) -- short notation, see below
FROM (
VALUES
((NULL::foo).pkid, (NULL::foo).x, (NULL::foo).y) -- subset of columns
, (1, 20, NULL)
, (2, 50, NULL)
) t(pkid, x, y) -- arbitrary column names (I made them match)
WHERE f.pkid = t.pkid;
正反两方面,比如4。,但我们可以处理任何列的子集,而不必知道完整的列表。
还显示
UPDATE
本身的简短语法,这对于具有许多列的情况很方便。相关:
所有列的批量更新
4.和5.是我的最爱。
分贝
强制类型转换 隐式类型转换:隐式类型转换又称为自动类型转换,隐式类型转换可分为三种:算术转换、赋值转换和输出转换。 显式类型转换:显式类型转换又称为强制类型转换,指的是使用强制类型转换运算符,将一个变量或表达式转化成所需的类型,这种类型转换可能会造成数据的精度丢失。 数据有不同的类型,不同类型数据之间进行混合运算时必然涉及到类型的转换问题。 转换的方法有两种: 自动转换(隐式转换):遵循一定的规则
我尝试通过methodhandles将方法链接在一起,其中一些方法来自泛型类型。如果函数返回泛型类型,我必须为MethodType指定Object.Class,但我看不到将其转换回泛型类型参数类型的简单方法。在大多数情况下,这没有问题,因为invoke似乎自动转换它们,但我必须创建mhs,它可以用InvokeExact运行。难道没有简单的方法使用MethodHandles进行强制转换吗? 我的测试
Go 语言是一种强类型语言,而且没有隐式转换。也就是说运算符左右的变量类型必须一致,若不一致就会报错。为了使用户更加的容易使用,所以 Go 语言提供了各个类型之间强制类型转换的内置方法。 1. 整型和浮点型互相转换 整型和浮点型变量之间的相互转换相对容易,只需使用强制类型转换标志 type(variable)就好了。需要注意的是,当高精度向低精度类型转换的时候,会失真。即浮点型向整型转换的时候会失
指针变量数据类型的强制转换 必须显式强制类型转换,不允许隐式类型转换 指向空间的强制类型转换,本质上就是普通变量的强制类型转换 int a = 10; float b = 3.14; int *pa = &a; float *pb = &b; *pa = (int)*pb; // 等价于 a = (int)b; 指针本身强制类型转换,改变的是对其指向空间的引用方式(空间大小和存储结构) int
例外情况: java.lang.ClassCastException:com.interconnect.library.gcm.util.checkplayServices(util.java:96),com.interconnect.library.gcm.regiseter.handleRegister(regiseter.java:53),com.interconnect.library.g
问题内容: 我不明白为什么在尝试执行以下操作时会收到警告(未经检查的演员表): 我的意思是将castedMap发布到外部代码的危险是什么?两种操作均可在运行时完美运行: 使用SomeType类型的键从castedMap获取元素 使用SomeType类型的键将元素放入castedMap中。 我只是使用@SuppressWarnings(“ unchecked”)取消警告。 问题答案: 答案可能很无聊