如何返回数据库中现有表中某一行的特定列值?

时间:2015-07-29 18:16:15

标签: sql postgresql foreign-keys case case-when

问题:

我正在PostgreSQL 9.0中工作,我很难搞清楚如何处理你想要返回特定行的特定列值以便在CASE WHEN THEN语句中使用的情况。

我想基本上进入并设置表A的值:someRow的someColumn值,等于表B的值:行X的列A值,给定行X列B的值。(更多细节见“Backround信息“如果需要了解问题”

这就是我想做的事(但不知道如何):

Update tableA
Set someColumn 
    CASE WHEN given_info_column = 'tableB: row X's column B value' 
    THEN (here I want to return row X's column A value, finding row X using the given column B value) 
    ELSE someColumn END

背景资料:(可选,澄清)

想象一下,在现有数据库中存在一个用户活动表和一个设备表,其中已经存在的活动执行了您正在使用的代码库中存在的字符串:(例如)

User_Activity:
id (int) | user_name (string)    | activity_preformed (string)            | category (string) 
---------|-----------------------|----------------------------------------|------------------
1        | Joe Martinez          | checked out iphone: iphone2            | dvc_activity
2        | Jon Shmoe             | uploads video from device: (id: 12345) | dvc_activity
3        | Larry David           | goes to the bathroom                   |other_activity

Device:
seq (int)| device_name (string)  | device_srl_num (int) | device_status (string)|
---------+-----------------------+----------------------+-----------------------+
1        | iphone1               | 12344                | available
2        | iphone2               | 12345                | checked out
3        | android1              | 23456                | available

你老板的任务是创建一个报告,显示一个包含所有设备活动的表,如下所示:

Device Activity Report
(int)     (int)    (string)       (string)                                 (string)      (int)         (string)
act_seq  |usr_id | usr_name     | act_performed                         |  dvc_name | dvc_srl_num | dvc_status  
---------+-------+--------------+---------------------------------------+-----------+-------------+------------
1        |1      | Joe Martinez | Checked out iphone: iphone2           | iphone2   | 12345       | checked out 
2        |2      | John Shmoe   | uploads video from device: (id: 12345)| android1  | 23456       | available    

出于此问题的目的,必须通过向名为dvc_seq的用户活动表添加新列来完成此操作,该列将是设备表的外键。您将通过从用户活动表中查询并加入User_Activity(dvc_seq)= Device(seq)

中的两个来创建临时表。

这很好,适用于User_Activity表中的新条目,该表将记录链接到关联设备的如果该活动涉及设备。

问题是您需要进入并填写User_Activity表中新dvc_seq列的值,以获取与设备相关的所有先前条目。由于之前的程序员决定使用序列号指定activity_performed列中的哪个设备以及其他时间的设备名称,这会产生一个有趣的问题,您需要从设备派生相关的设备序列号,并给出其名称或序列号。

再一次,我想做什么:(使用这个例子)

UPDATE User_Activity 
    SET dvc_seq 
    CASE WHEN activity_performed LIKE 'checked out iphone:%' 
    THEN (seq column of Device table) 
        WHERE (SELECT 1 FROM Device WHERE device_name = (substring in place of the %))
    ELSE dvc_seq (I think this would be null since there would be nothing here yet)  
    END

你们任何人都可以帮助我实现这个目标吗?提前感谢所有回复和建议!

3 个答案:

答案 0 :(得分:2)

当activity_performed中包含序列号或名称时,下面的查询使用update-join来更新序列号

UPDATE UserActivity
SET a.dvc_seq = b.seq
FROM UserActivity AS a
JOIN devices b 
    ON UserActivity.activity_performed LIKE '%from device: (id: ' || b.serial_num || '%'
    OR UserActivity.activity_performed LIKE '%: ' || b.name || '%'

答案 1 :(得分:1)

关于如何根据@FuzzyTree给出的正确答案加速此代码的另一个更新(这仅适用于具有标准长度的序列号,而不适用于可能很多的设备名称不同的尺寸)

由于连接中使用了LIKE,因此对于大型数据库,查询运行速度非常慢。一个更好的解决方案是利用postgres substring()position()函数,并将序列号上的表连接起来,如下所示:

UPDATE UserActivity
SET a.dvc_seq = b.seq
FROM UserActivity AS a
JOIN devices b 
    ON b.serial_num = 
    substring(activity_performed from position('%from device: (id: ' in activity_performed)+(length of the string before the plus so that position returns the start position for the serial number)) for (lengthOfSerialNumberHere)) 
WHERE UserActivity.activity_performed LIKE '%from device: (id: ' || b.serial_num || '%';`

答案 2 :(得分:0)

在postgresql中,您无法执行复杂的CASE表达式,如

CASE WHEN activity_performed LIKE 'checked out iphone:%' 

CASE WHEN 1, 2

您可以做的最好的事情是创建一个功能

UPDATE User_Activity 
SET dvc_seq = getDeviceID(User_Activity.activity_preformed);

在这里你可以做到IF,CASE更容易

CREATE OR REPLACE FUNCTION getDeviceID(activity text)
RETURNS integer AS
$BODY$
DECLARE 
    device_name text;
    device_id   integer;

BEGIN
    -- parse the string activity 

    /* this part is pseudo code
    set device_id;
    IF (device_id is null)
       set device_name;
       search for device_id using device_name;
       set device_id;
    */
    RETURN device_id;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
ALTER FUNCTION get_box(text)
  OWNER TO postgres;