如何在一个动态选择查询中编写多个查询

时间:2015-12-29 13:04:10

标签: mysql plsql rdbms

我在表格中有多条记录:

  UserTable:
          UID         AGE          Contact

          22           61          8899778899
          14           45          8877556644
          16           75          7894561246

我想编写一个程序,它将接受UID列表并返回相应的联系号码。

    Input         output
 <22,14,16>        <(22,8899778899),(14,8877556644),(16,7894561246)>

我尝试过以下程序,

     drop procedure if exists testWC;
     CREATE  PROCEDURE testWC(Array_String VARCHAR(100))
         BEGIN
            SELECT UID,Contact FROM UserTable
            WHERE UID IN (Array_String);

         END

  call testWC('22,14,16');

   Result:
         UID           Contact

         22            8899778899

但它在列表中只占22,即字符串列表中的第一个元素。帮助我提出宝贵的建议。

2 个答案:

答案 0 :(得分:1)

我不知道为什么它不起作用。我在类似的情况下使用预备语句,在这种情况下也适用。

 CREATE  PROCEDURE testWC(Array_String VARCHAR(100))
     BEGIN
        SET @stmt = CONCAT('SELECT UID, Contact FROM UserTable WHERE UID IN (', Array_String, ')');

        PREPARE q FROM @stmt;
        EXECUTE q;
        DEALLOCATE PREPARE q;


     END //

我允许您根据需要格式化结果。

答案 1 :(得分:1)

IN子句不接受此类查询,因为您实际提供的是值为'22,14,16'的VARCHAR2参数。 最好的方法是将ARRAY类型参数传递给过程。 以下示例

CREATE TYPE NUMBER_10_ARRAY IS TABLE OF NUMBER(10);
CREATE TYPE T_UID_CONTACT IS RECORD (uid NUMBER, contact NUMBER);
CREATE TYPE T_UID_CONTACT_ARRAY IS TABLE OF T_UID_CONTACT;

CREATE OR REPLACE PROCEDURE testWS(p_uids NUMBER_10_ARRAY)
IS
  lt_uid_contacts     T_UID_CONTACT := NEW T_UID_CONTACT();
  CURSOR c_fetch_uid_contacts IS
  SELECT
    uid
    ,contact
  FROM
    user_table
  WHERE
    uid IN ((SELECT column_value FROM TABLE(p_uids)));

BEGIN
  OPEN c_fetch_uid_contacts;
  FETCH c_fetch_uid_contacts BULK COLLECT INTO lt_uid_contacts;
  CLOSE c_fetch_uid_contacts;

  -- Now you have all the records in your lt_uid_contacts variable
END testWS;

您还可以撰写pipelined function,以便能够通过简单的查询检索结果。

CREATE OR REPLACE FUNCTION testWS(p_uids NUMBER_10_ARRAY) RETURN T_UID_CONTACT PIPELINED
IS
  lt_uid_contacts     T_UID_CONTACT := NEW T_UID_CONTACT();
  CURSOR c_fetch_uid_contacts IS
  SELECT
    uid
    ,contact
  FROM
    user_table
  WHERE
    uid IN ((SELECT column_value FROM TABLE(p_uids)));

BEGIN
  -- Fetch all rows at once (you switch PL/SQL - SQL context only once)
  OPEN c_fetch_uid_contacts;
  FETCH c_fetch_uid_contacts BULK COLLECT INTO lt_uid_contacts;
  CLOSE c_fetch_uid_contacts;

  -- If any rows had been fetched, pipe rows so the pipelined function could return them
  IF lt_uid_contacts.COUNT > 0 THEN
    FOR idx IN lt_uid_contacts.FIRST .. lt_uid_contacts.LAST
    LOOP
      PIPE ROW(lt_uid_contacts(idx));
    END LOOP;
  END IF;

  RETURN;

  -- Handle NO_DATA_NEEDE exception, fired only in PIPELINED function in case SELECT statement using this function
  -- didn't need the entire set of rows (e.g. limited by the WHERE clause)
  EXCEPTION
    WHEN NO_DATA_NEEDED THEN
      NULL; -- exception is ignored in this case, but generally, it is a terrible practice to ignore any exceptions like this
            -- if anytime you didn't know what to do on exception, it's better not to handle that exception at all and let the error occur
END testWS;


-- Then you can just use a SELECT query
SELECT 
  uid
  ,contact
FROM 
  TABLE(
    testWS(
      p_uids => CAST(
                  MULTISET((SELECT DECODE(LEVEL,1,22,2,14,3,16) FROM dual CONNECT BY LEVEL < 4))
                  AS NUMBER_10_ARRAY
                )

    )
  ) tab;

但是如果你真的想要将参数保持为由逗号分隔值组成的VARCHAR2,你可以使用下面的查询:

WITH uids AS (
  SELECT
    REPLACE(REGEXP_SUBSTR(Array_String ,',?\d+,?',1,LEVEL),',','') AS the_id
  FROM
    text
  CONNECT BY LEVEL <= REGEXP_COUNT(Array_String ,',')+1
)
SELECT
  ut.uid
  ,ut.contact
FROM
  user_table ut
  ,uids
WHERE
  ut.uid = uids.the_id;