如何根据情况将PostgreSQL的多个值转换为多个变量?

问题描述 投票:1回答:1

我想知道是否有可能在postgres的SELECT语句中使用CASE表达式,在该语句中我一次写入多个值。

例如:

DO
$$
DECLARE
    var int := 1;
    val1 int;
BEGIN
    SELECT
    CASE var
    WHEN 1 THEN
        variable1, variable2
    WHEN 2 THEN
        variable3, variable4
    INTO 
        val1, val2
    FROM mytable;
$$

事情是使用它时效果很好:

DO
$$
DECLARE
    var int := 1;
    val1 int;
    val2 int;
BEGIN
    SELECT
    CASE var
    WHEN 1 THEN
        variable1
    WHEN 2 THEN
        variable3
    INTO 
        val1
    FROM mytable;
$$

我知道我可以将CASE移到SELECT语句之外,只编写两个SELECT语句,但这似乎比我想要的解决方案要写的多。

我尝试的另一件事是:

DO
$$
DECLARE
    var int := 1;
    val1 int;
BEGIN
    SELECT
    CASE var
    WHEN 1 THEN
        (variable1, variable2)
    WHEN 2 THEN
        (variable3, variable4)
    INTO 
        val1, val2
    FROM mytable;
$$

但是这似乎将(variable1,variable2)写入val1,并且由于数据类型错误而失败。

我猜用最后一个解决方案,我可以将值写入数组,但是我需要在单独的变量中使用它们。

感谢任何提示。

postgresql select case
1个回答
0
投票

我可以考虑三种方式。有两个很简单,一个很复杂,但与(variable1, variable2)有关。

  1. CASE用法(简单)
  2. JSONB用法(简单)
  3. [TYPE用法(复杂)

首选:CASE用法

这是最简单但不是真正优化的选项。

当使用SELECT vals INTO vars时,必须具有相同数量的val和var。因此,在此选项中,每个值都需要CASE

-- CASE QUERY
DO $$
DECLARE
-- control var
    var integer := 1;
-- result vars
    var1 integer := 1;
    var2 integer := 2;
    var3 integer := 3;
    var4 integer := 4;
-- final values
    val1 integer;
    val2 integer;
BEGIN
    SELECT
        CASE var -- CASE for val1
            WHEN 1 THEN var1
            WHEN 2 THEN var3 END,
        CASE var -- CASE for val2
            WHEN 1 THEN var2 
            WHEN 2 THEN var4 END
    INTO val1,val2;

    RAISE NOTICE '%',val1; -- outputs: 1
    RAISE NOTICE '%',val2; -- outputs: 2
END;$$

如果var更改为'2',则输出将为3和4。

您在这里更改代码

CASE var
    WHEN 1 THEN
        variable1, variable2
    WHEN 2 THEN
        variable3, variable4

to

CASE var -- CASE for val1
    WHEN 1 THEN variable1
    WHEN 2 THEN variable3 END,
CASE var -- CASE for val2
    WHEN 1 THEN variable2
    WHEN 2 THEN variable4 END

如果添加更多变量,则添加更多案例。

第二选项:JSONB用法

此选项是最佳方法,因为您不必编写许多CASE子句,也不必为此过程创建额外的步骤。

基本上,您使用的JSONB变量具有这样的所有所需变量:

{
   "val1": 1,
   "val2": 2
}

这是您的操作方式:

-- USING JSONB
DO $$
DECLARE
-- control var
    var integer := 1;
-- result vars
    var1 integer := 1;
    var2 integer := 2;
    var3 integer := 3;
    var4 integer := 4;
-- JSON var that will have val1 and val2 data
    jsonvar jsonb;
BEGIN
    SELECT CASE var 
            WHEN 1 THEN CAST('{"val1":'||var1||',"val2":'||var2||'}' as jsonb)
            WHEN 2 THEN CAST('{"val1":'||var3||',"val2":'||var4||'}' as jsonb) END
    INTO jsonvar;

    RAISE NOTICE '%',jsonvar->>'val1'; -- outputs: 1
    RAISE NOTICE '%',jsonvar->>'val2'; -- outputs: 2
END;$$

请注意,您将JSON对象插入类型为JSONB的变量中。将相同数量的var转换为val。

因为JSON实际上是一个灵活的对象,所以要添加更多变量,您只需要相应地调整JSON。

在PostgreSQL中,最好使用JSONB而不是普通的JSON。手册指出:

json数据类型存储输入文本的精确副本,其中处理功能必须在每次执行时重新解析;而jsonb数据以分解后的二进制格式存储,因此速度稍慢由于增加了转换开销而无法输入,但速度要快得多流程,因为不需要重新解析。 jsonb还支持索引,这可能是一个显着的优势。

第三选项:TYPE用法

此选项是更复杂的选项,因为我们输入了RECORD地形。是的,PostgreSQL中的(data1,data2)RECORD。什么是记录?简单来说,就是没有数据结构的ROW

RECORD是什么意思?好了,要清楚一点,例如,在创建表时:

CREATE TABLE data(val1 integer,val2 integer);

如果要在表“数据”中插入数据,则必须插入记录,所以当您这样做时:

INSERT INTO data(val1,val2) VALUES (1,2);

您在数据中插入RECORD (1,2)

注释 1和2在括号 (1,2)内。因此,当您编写(variable1,variable2)时,将制作包含变量1和变量2的RECORD

根据手册

记录变量类似于行类型变量,但它们没有预定义的结构 ...

RECORD不是真正的数据类型,只是一个占位符。

这里是问题。 RECORD没有结构,因此PostgreSQL不知道如何解析它。当您使用INSERT INTO时,您会告诉PostgreSQL结构,因此记录将采用表的结构(上一个示例)。

[执行时:

SELECT
    CASE var
    WHEN 1 THEN
        (variable1, variable2)
    WHEN 2 THEN
        (variable3, variable4)

您选择的RECORD

为简化起见,如果您执行SELECT (1,2),您会得到:

row (record)
------------
(1,2)

如何将结构分配给ROW?好吧,您可以使用SELECTFOR或使用TYPESELECTFOR的用法类似于INSERT INTO,您知道数据结构。在这种情况下,没有参考,因此必须使用TYPE

在PostgreSQL中,您可以创建个人数据类型。因此,您可以执行以下操作:

CREATE TYPE mytype AS (val1 integer, val2 integer);-- execute only once

类似于表,您一次可以创建一个TYPE,并且TYPE在整个数据库中都可用。要删除类型,只需执行DROP TYPE mytype;

[mytype具有变量所需的两个值,例如表,TYPE可以具有任何具有任何数据类型的“列”。

现在,如果您执行SELECT (1,2)::mytype,您将得到:

row (record)
------------
(1,2)

仍然显示一行,因为PostgreSQL不知道如何解析它。

但是如果您执行SELECT * FROM (VALUES(1,2)) AS mytype(val1,val2);,则会得到

 val1 | val2
------+------
    1 |    2

这是因为您正在告诉PostgreSQL如何解析它(注意使用VALUES)。

这表明将结构分配给记录行不是那么容易。但是可能如下所示:

-- USING TYPES
-- Requires mytype created
DO $$
DECLARE
-- control var
    var integer := 1;
-- result vars
    var1 integer := 1;
    var2 integer := 2;
    var3 integer := 3;
    var4 integer := 4;
-- final values
    val1 integer;
    val2 integer;
BEGIN
    SELECT x[1].val1,x[1].val2
    FROM(
        SELECT ARRAY(
            SELECT CASE var
                WHEN 1 THEN (var1,var2)::mytype 
                WHEN 2 THEN (var3,var4)::mytype END
        )::mytype[] AS x
    )dataset
    INTO val1,val2;

    RAISE NOTICE '%',val1; -- outputs: 1
    RAISE NOTICE '%',val2; -- outputs: 2
END;$$

关键部分是使用阵列。

执行时:

SELECT x.val1,x.val2
        FROM(
            SELECT CASE 1
                WHEN 1 THEN (1,2)::mytype
                WHEN 2 THEN (3,4)::mytype END AS X
        )dataset

您收到此错误:

ERROR:  missing FROM-clause entry for table "x"

PostgreSQL不知道如何解析它,所以您告诉PostgreSQL通过一个数组来获取它。

SELECT
x[1].val1,x[1].val2
FROM(
    SELECT ARRAY(
        SELECT CASE 1
           WHEN 1 THEN (1,2)::mytype
           WHEN 2 THEN (4,5)::mytype END
    )::mytype[] AS x
) dataset

此输出:

 val1 | val2
------+------
    1 |    2

此选项的问题是您创建的TYPE是静态的,因此,如果必须更改它,则必须删除该类型并再次创建它。

但是您就是这样做的。第二种选择是最好的,也是一种更现代的方法。

© www.soinside.com 2019 - 2024. All rights reserved.