Select lines where nested table column meets a condition - sql

v is defined as follows: create or replace type v is table of number and emp is a table which contains a column of type v.
I want to select the lines where v.count is 3, but I will get a compilation error. Is it because v.count is PL/SQL code?
I tried putting the code inside an anonymous block but it still didn't work.
Is using cursors the only solution?
SELECT *
FROM emp
WHERE V.COUNT = 3;
Thanks.

I think you're looking for cardinality():
CARDINALITY returns the number of elements in a nested table. The return type is NUMBER. If the nested table is empty, or is a null collection, then CARDINALITY returns NULL.
So you can do:
SELECT *
FROM emp
WHERE cardinality(V) = 3;
Quick demo:
create or replace type v is table of number
/
create table emp (id number, v v)
nested table v store as v_tab;
insert into emp (id, v) values (1, v(1));
insert into emp (id, v) values (2, v(1,2));
insert into emp (id, v) values (3, v(1,2,3));
insert into emp (id, v) values (4, v(1,2,3,4));
column v format a30
set feedback 1
SELECT *
FROM emp
WHERE cardinality(V) = 3;
ID V
---------- ------------------------------
3 V(1, 2, 3)
1 row selected.

I like Alex's cardinality answer, here is another approach:
create or replace type num_type as table of number;
create table table_with_num_type
(
ids num_type,
val varchar2(100)
)
nested table ids store as ids_tab ;
insert into table_with_num_type(ids, val) values (num_type(1,2,3), 'TEST1');
insert into table_with_num_type(ids, val) values (num_type(4,5,6,7), 'TEST2');
commit;
select t.val, count(t2.column_value) as num_count
from table_with_num_type t, table(t.ids) t2
group by t.val
having count(t2.column_value) = 3;
Result:
VAL NUM_COUNT
TEST1 3

Related

Insert into table from select only when select returns valid rows

I want to insert into table from select statement but it is required that insert only happens when select returns valid rows. If no rows return from select, then no insertion happens.
insert into products (name, type) select 'product_name', type from prototype where id = 1
However, the above sql does insertion even when select returns no rows.
It tries to insert NULL values.
I know the following sql can check if row exists
select exists (select true from prototype where id = 1)
How to write a single SQL to add the above condition to insert to exclude the case ?
You are inserting the wrong way. See the example below, that doesn't insert any row since none matches id = 1:
create table products (
name varchar(10),
type varchar(10)
);
create table prototype (
id int,
name varchar(10),
type varchar(10)
);
insert into prototype (id, name, type) values (5, 'product5', 'type5');
insert into prototype (id, name, type) values (7, 'product7', 'type7');
insert into products (name, type) select name, type from prototype where id = 1
-- no rows were inserted.

Insert 1000 rows in single sql statment or procedure

How should i write a single sql statement or a stored procedure,
To insert 1000 values in 1000 rows and same column with each column having different values (among those 1000)
Here is the query i wrote,
INSERT INTO a_b values
(
(SELECT max(a_b_id) + 1 from a_b),
1111,
(SELECT s_id FROM a_b WHERE s_id in ('0','1','2','3','4')),
0,
1,
sysdate,
sysdate,
0,
1,
null
);
like say, i have 1000 s_id's i want select them one by one and insert them in one particular column, each time creating a new row.
EX, in first row s_id should be 0 then in second row it should be 1 like that goes on till thousand, attached an image of sample database i am working with.
You can use connect by for this:
INSERT INTO a_b (s_id, col2, col3, ....)
select level, --<< this value will change for every row
1111,
sysdate,
... more columns ...
from dual
connect by level <= 1000;
you can use cross apply to get 1000 rows along with 1000 other columns to insert 1000 rows as below:
insert into a_b (column names...)
select (max(a_b_id) over()) +1 as MaxId, s_id from a_b a cross apply (select 0, 1,SYSDATETIME, SYSDATETIME, 0, 1, null) b where a.s_id('111','222')--condition
The below is a syntax error. You will never get something like that to work.
create table fff
( id int not null
);
insert fff values (select 1,7777,select 3, select 3);
So you need to break it up into chunks
DROP PROCEDURE IF EXISTS uspRunMe;
DELIMITER $$
CREATE PROCEDURE uspRunMe()
BEGIN
insert into a_b select max(a_b_id) + 1 from a_b;
insert into a_b values (1111);
insert into a_b SELECT s_id FROM a_b WHERE s_id in ('0','1','2','3','4');
insert into a_b values (0,1);
insert into a_b select sysdate,sysdate;
insert into a_b values (0,1,null);
END;$$
DELIMITER ;
Test it:
call uspRunMe();
The above is for MySQL. You have a few db engines tagged here.

Show row number column from result set in oracle sql view

I want to add an extra column so if I get, let's say, 4 rows in the result this column will have values 1,2,3,4.
I've tried ROWNUM, but since this is a view it shows the actual row number in the whole view, and that's not what I want.
Here is a sample schema:
CREATE TABLE TEST (RID NUMBER, RVAL VARCHAR2(100 BYTE));
INSERT INTO TEST (RID, RVAL) VALUES (1, 'ONE');
INSERT INTO TEST (RID, RVAL) VALUES (2, 'TWO');
INSERT INTO TEST (RID, RVAL) VALUES (3, 'THREE');
INSERT INTO TEST (RID, RVAL) VALUES (4, 'FOUR');
CREATE OR REPLACE VIEW VTEST AS
SELECT ROWNUM AS NUMROW, RID, RVAL FROM TEST;
Here are two sample queries. The first shows the result I want. The second how I want to get it (with a simple select against the view)
SELECT ROWNUM,RID,RVAL FROM TEST WHERE RID = 3 OR RID = 4;
SELECT * FROM VTEST WHERE RID = 3 OR RID = 4;
Here is the fiddle: http://sqlfiddle.com/#!4/4e816/3
in oracle use ROW_NUMBER oracle docs

PL/SQL MERGE Using Collection

I am having trouble merging a table with a collection.
Let's say I have a table emp.
Here is my PL/SQL code snippet.
TYPE empcol is table of emp%ROWTYPE INDEX BY BINARY_INTEGER;
tmpemp empcol;
-- Code here to load data from a CSV file into tmpemp collection
-- tmpemp(1).emp_id := parsedstring
-- etc.
MERGE INTO emp A using tmpemp B ON A.emp_id = B.emp_id
WHEN MATCHED THEN UPDATE SET A.fname = B.fname, A.lname = B.lname
WHEN NOT MATCHED THEN INSERT (emp_id, fname, lname) VALUES (b.emp_id, b.fname, b.lname);
Compiler doesn't like it. Its throwing ORA-0942 - Table or View doesn't exist.
What am I doing wrong? or How can I do this better.
Thanks a lot for any help you can provide.
PL/SQL types like emp%ROWTYPE or TABLE OF ... INDEX BY ... cannot be used in SQL queries.
The type must be declared as SQL type (not as PL/SQL type) to be used in SQL query.
Try this approach (example):
create table emp(
firstname varchar2(100),
salary number
);
insert into emp values( 'John', 100 );
commit;
create type my_emp_obj is object(
firstname varchar2(100),
salary number
);
/
create type my_emp_obj_table is table of my_emp_obj;
/
declare
my_emp_tab my_emp_obj_table;
begin
null;
my_emp_tab := my_emp_obj_table( my_emp_obj( 'John', 200 ), my_emp_obj( 'Tom', 300 ));
MERGE INTO emp
USING ( SELECT * FROM TABLE( my_emp_tab )) src
ON ( emp.firstname = src.firstname )
WHEN MATCHED THEN UPDATE SET salary = src.salary
WHEN NOT MATCHED THEN INSERT VALUES( src.firstname, src.salary );
end;
/
select * from emp;
FIRSTNAME SALARY
----------------------- ----------
John 200
Tom 300

How to simulate group_concat in plain sql

I am using the hxtt sql driver for csv files. It only supports plain sql. is there a way to simulate group concat using plain sql statements?
How plain? If you can use triggers then you can do it fairly simply. I've used this trick before in SQLite3 when I've needed a group_concat() that allows me to specify the order in which values are to be concatenated (SQLite3 does not provide a way to do that).
So let's say we have a table like this:
CREATE TABLE t(v TEXT NOT NULL, num INTEGER NOT NULL UNIQUE);
and you want to concatenate the values of v ordered by num, with some separator character, let's say a comma.
CREATE TEMP TABLE c(v TEXT);
CREATE TEMP TABLE j(v TEXT);
CREATE TEMP TRIGGER j_ins BEFORE INSERT ON j
FOR EACH ROW
BEGIN
UPDATE c SET v = v || ',' || NEW.v;
INSERT INTO c (v) SELECT NEW.v WHERE NOT EXISTS (SELECT * FROM c);
SELECT RAISE(IGNORE);
END;
Now we can:
INSERT INTO j (c) SELECT v FROM t ORDER BY num;
SELECT v FROM c; -- this should output the concatenation of the values of v in t
DELETE FROM c;
Finally, this is a sqlite3 session showing that this works:
SQLite version 3.7.4
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> CREATE TABLE t(v TEXT NOT NULL, num INTEGER NOT NULL UNIQUE);
sqlite> CREATE TEMP TABLE c(v TEXT);
sqlite> CREATE TEMP TABLE j(v TEXT);
sqlite> CREATE TEMP TRIGGER j_ins BEFORE INSERT ON j
...> FOR EACH ROW
...> BEGIN
...> UPDATE c SET v = v || ',' || NEW.v;
...> INSERT INTO c (v) SELECT NEW.v WHERE NOT EXISTS (SELECT * FROM c);
...> SELECT RAISE(IGNORE);
...> END;
sqlite> insert into t (v, num) values (1, 0);
sqlite> insert into t (v, num) values (31, 1);
sqlite> insert into t (v, num) values (52, 2);
sqlite> insert into t (v, num) values (0, 3);
sqlite> SELECT v FROM c;
sqlite> INSERT INTO j (v) SELECT v FROM t ORDER BY num;
sqlite> SELECT v FROM c;
1,31,52,0
sqlite> SELECT v FROM j;
sqlite> DELETE FROM c;
sqlite>
Now, this isn't pure SQL, because it depends on triggers. Between recursive triggers and all the ways to do conditionals in SQL you have a Turing complete system. But if you don't have triggers, or any procedural extensions, no generator tables... then not so much.
I know nothing about hxtt, so maybe this won't help you. But SQLite3 can deal with CSV, so maybe SQLite3 can help you...