Inserting rows where column can have many values - sql

I am writing a stored proc that inserts rows into a table. The issue is that many of the columns can have a list of different values and all of the rows in the db need to reflect these values. For example:
I have a table: Table1(state, number)
state will need to be 1-50 as its value and number is 1-3. There needs to be a row for each state with each number.
(1,1)
(1,2)
(1,3)
(2,1)...etc
There has got to be a nice way to do this but my research has not been fruitful. Does anyone have any suggestions?

A good way to generate the values is using a cross join. Here is an example:
insert into table(state, number)
select s.state, n.number
from (select 'AK' as state union all select 'AL' union all . . .
) s cross join
(select 1 as number union all select 2 union all select 3
) n
You may already have a lists of states and/or numbers, in which case you can use this. For example:
insert into table(state, number)
select s.state, n.number
from (select state from states
) s cross join
(select 1 as number union all select 2 union all select 3
) n

Your need is a cross join between two tables, one containing 50 rows, the other 3 rows.
In Oracle:
select *
from
(
select rownum as state
from dual
connect by rownum <= 50
) t1
,
(
select rownum as num
from dual
connect by rownum <= 3
) t2
Fiddle

Related

SQLite - Return Rows Even If They Are Duplicates

I have a simple SQLite table which has just one ID column.
I have some variable IDs that may be duplicates of each other like: 1,2,3,4,3,1 (These IDs are just examples, there could be hundreds of them).
And I have a simple query as follows:
SELECT ID FROM TABLE WHERE ID in (1,2,3,4,3,1)
In the usual case the answer contains only 4 rows with ids 1,2,3,4. Is there any way to force SQLite to return rows in the order of the request (1,2,3,4,3,1) even if they are duplicates?
I have n IDs in my query and I want n rows in return even if they are duplicates.
Edit: The Table Definition is:
CREATE TABLE TEST(ID TEXT PRIMARY KEY)
You can use left join:
select t.*
from (select 1 as id, 1 as ord union all
select 2 as id, 2 as ord union all
select 3 as id, 3 as ord union all
select 4 as id, 4 as ord union all
select 3 as id, 5 as ord union all
select 1 as id, 6 as ord
) ids left join
t
on t.id = ids.id
order by ids.ord;

How to join two tables with the same number of rows in SQLite?

I have almost the same problem as described in this question. I have two tables with the same number of rows, and I would like to join them together one by one.
The tables are ordered, and I would like to keep this order after the join, if it is possible.
There is a rowid based solution for MSSql, but in SQLite rowid can not be used if the table is coming from a WITH statement (or RECURSIVE WITH).
It is guaranteed that the two tables have the exact same number of rows, but this number is not known beforehand. It is also important to note, that the same element may occur more than twice. The results are ordered, but none of the columns are unique.
Example code:
WITH
table_a (n) AS (
SELECT 2
UNION ALL
SELECT 4
UNION ALL
SELECT 5
),
table_b (s) AS (
SELECT 'valuex'
UNION ALL
SELECT 'valuey'
UNION ALL
SELECT 'valuez'
)
SELECT table_a.n, table_b.s
FROM table_a
LEFT JOIN table_b ON ( table_a.rowid = table_b.rowid )
The result I would like to achieve is:
(2, 'valuex'),
(4, 'valuey'),
(5, 'valuez')
SQLFiddle: http://sqlfiddle.com/#!5/9eecb7/6888
This is quite complicated in SQLite -- because you are allowing duplicates. But you can do it. Here is the idea:
Summarize the table by the values.
For each value, get the count and offset from the beginning of the values.
Then use a join to associate the values and figure out the overlap.
Finally use a recursive CTE to extract the values that you want.
The following code assumes that n and s are ordered -- as you specify in your question. However, it would work (with small modifications) if another column specified the ordering.
You will notice that I have included duplicates in the sample data:
WITH table_a (n) AS (
SELECT 2 UNION ALL
SELECT 4 UNION ALL
SELECT 4 UNION ALL
SELECT 4 UNION ALL
SELECT 5
),
table_b (s) AS (
SELECT 'valuex' UNION ALL
SELECT 'valuey' UNION ALL
SELECT 'valuey' UNION ALL
SELECT 'valuez' UNION ALL
SELECT 'valuez'
),
a as (
select a.n, count(*) as a_cnt,
(select count(*) from table_a a2 where a2.n < a.n) as a_offset
from table_a a
group by a.n
),
b as (
select b.s, count(*) as b_cnt,
(select count(*) from table_b b2 where b2.s < b.s) as b_offset
from table_b b
group by b.s
),
ab as (
select a.*, b.*,
max(a.a_offset, b.b_offset) as offset,
min(a.a_offset + a.a_cnt, b.b_offset + b.b_cnt) - max(a.a_offset, b.b_offset) as cnt
from a join
b
on a.a_offset + a.a_cnt - 1 >= b.b_offset and
a.a_offset <= b.b_offset + b.b_cnt - 1
),
cte as (
select n, s, offset, cnt, 1 as ind
from ab
union all
select n, s, offset, cnt, ind + 1
from cte
where ind < cnt
)
select n, s
from cte
order by n, s;
Here is a DB Fiddle showing the results.
I should note that this would be much simpler in almost any other database, using window functions (or perhaps variables in MySQL).
Since the tables are ordered, you can add row_id values by comparing n values.
But still the best way in order to get better performance would be inserting the ID values while creating the tables.
http://sqlfiddle.com/#!5/9eecb7/7014
WITH
table_a_a (n, id) AS
(
WITH table_a (n) AS
(
SELECT 2
UNION ALL
SELECT 4
UNION ALL
SELECT 5
)
SELECT table_a.n, (select count(1) from table_a b where b.n <= table_a.n) id
FROM table_a
) ,
table_b_b (n, id) AS
(
WITH table_a (n) AS
(
SELECT 'valuex'
UNION ALL
SELECT 'valuey'
UNION ALL
SELECT 'valuez'
)
SELECT table_a.n, (select count(1) from table_a b where b.n <= table_a.n) id
FROM table_a
)
select table_a_a.n,table_b_b.n from table_a_a,table_b_b where table_a_a.ID = table_b_b.ID
or convert the input set to comma separated list and try like this:
http://sqlfiddle.com/#!5/9eecb7/7337
WITH RECURSIVE table_b( id,element, remainder ) AS (
SELECT 0,NULL AS element, 'valuex,valuey,valuz,valuz' AS remainder
UNION ALL
SELECT id+1,
CASE
WHEN INSTR( remainder, ',' )>0 THEN
SUBSTR( remainder, 0, INSTR( remainder, ',' ) )
ELSE
remainder
END AS element,
CASE
WHEN INSTR( remainder, ',' )>0 THEN
SUBSTR( remainder, INSTR( remainder, ',' )+1 )
ELSE
NULL
END AS remainder
FROM table_b
WHERE remainder IS NOT NULL
),
table_a( id,element, remainder ) AS (
SELECT 0,NULL AS element, '2,4,5,7' AS remainder
UNION ALL
SELECT id+1,
CASE
WHEN INSTR( remainder, ',' )>0 THEN
SUBSTR( remainder, 0, INSTR( remainder, ',' ) )
ELSE
remainder
END AS element,
CASE
WHEN INSTR( remainder, ',' )>0 THEN
SUBSTR( remainder, INSTR( remainder, ',' )+1 )
ELSE
NULL
END AS remainder
FROM table_a
WHERE remainder IS NOT NULL
)
SELECT table_b.element, table_a.element FROM table_b, table_a WHERE table_a.element IS NOT NULL and table_a.id = table_b.id;
SQL
SELECT a1.n, b1.s
FROM table_a a1
LEFT JOIN table_b b1
ON (SELECT COUNT(*) FROM table_a a2 WHERE a2.n <= a1.n) =
(SELECT COUNT(*) FROM table_b b2 WHERE b2.s <= b1.s)
Explanation
The query simply counts the number of rows up until the current one for each table (based on the ordering column) and joins on this value.
Demo
See SQL Fiddle demo.
Assumptions
A single column in used for the ordering in each table. (But the query could easily be modified to allow multiple ordering columns).
The ordering values in each table are unique.
The values in the ordering column aren't necessarily the same between the two tables.
It is known that table_a contains either the same or more rows than table_b. (If this isn't the case then a FULL OUTER JOIN would need to be emulated since SQLite doesn't provide one.)
No further changes to the table structure are allowed. (If they are, it would be more efficient to have pre-populated columns for the ordering).
Either way...
Use something like
WITH
v_table_a (n, rowid) AS (
SELECT 2, 1
UNION ALL
SELECT 4, 2
UNION ALL
SELECT 5, 3
),
v_table_b (s, rowid) AS (
SELECT 'valuex', 1
UNION ALL
SELECT 'valuey', 2
UNION ALL
SELECT 'valuez', 3
)
SELECT v_table_a.n, v_table_b.s
FROM v_table_a
LEFT JOIN v_table_b ON ( v_table_a.rowid = v_table_b.rowid );
for "virtual" tables (with WITH or without),
WITH RECURSIVE vr_table_a (n, rowid) AS (
VALUES (2, 1)
UNION ALL
SELECT n + 2, rowid + 1 FROM vr_table_a WHERE rowid < 3
)
, vr_table_b (s, rowid) AS (
VALUES ('I', 1)
UNION ALL
SELECT s || 'I', rowid + 1 FROM vr_table_b WHERE rowid < 3
)
SELECT vr_table_a.n, vr_table_b.s
FROM vr_table_a
LEFT JOIN vr_table_b ON ( vr_table_a.rowid = vr_table_b.rowid );
for "virtual" tables using recursive WITHs (in this example the values are others then yours, but I guess you get the point) and
CREATE TABLE p_table_a (n INT);
INSERT INTO p_table_a VALUES (2), (4), (5);
CREATE TABLE p_table_b (s VARCHAR(6));
INSERT INTO p_table_b VALUES ('valuex'), ('valuey'), ('valuez');
SELECT p_table_a.n, p_table_b.s
FROM p_table_a
LEFT JOIN p_table_b ON ( p_table_a.rowid = p_table_b.rowid );
for physical tables.
I'd be careful with the last one though. A quick test shows, that the numbers of rowid are a) reused -- when some rows are deleted and others are inserted, the inserted rows get the rowids from the old rows (i.e. rowid in SQLite isn't unique past the lifetime of a row, whereas e.g. Oracle's rowid AFAIR is) -- and b) corresponds to the order of insertion. But I don't know and didn't find a clue in the documentation, if that's guaranteed or is subject to change in other/future implementations. Or maybe it's just a mere coincidence in my test environment.
(In general physical order of rows may be subject to change (even within the same database using the same DMBS as a result of some reorganization) and is therefore no good choice to rely on. And it's not guaranteed, a query will return the result ordered by physical position in the table as well (it might use the order of some index instead or have a partial result ordered some other way influencing the output's order). Consider designing your tables using common (sort) keys in corresponding rows for ordering and to join on.)
You can create temp tables to carry CTE data row. then JOIN them by sqlite row_id column.
CREATE TEMP TABLE temp_a(n integer);
CREATE TEMP TABLE temp_b(n VARCHAR(255));
WITH table_a(n) AS (
SELECT 2 n
UNION ALL
SELECT 4
UNION ALL
SELECT 5
UNION ALL
SELECT 5
)
INSERT INTO temp_a (n) SELECT n FROM table_a;
WITH table_b (n) AS
(
SELECT 'valuex'
UNION ALL
SELECT 'valuey'
UNION ALL
SELECT 'valuez'
UNION ALL
SELECT 'valuew'
)
INSERT INTO temp_b (n) SELECT n FROM table_b;
SELECT *
FROM temp_a a
INNER JOIN temp_b b on a.rowid = b.rowid;
sqlfiddle:http://sqlfiddle.com/#!5/9eecb7/7252
It is possible to use the rowid inside a with statement but you need to select it and make it available to the query using it.
Something like this:
with tablea AS (
select id, rowid AS rid from someids),
tableb AS (
select details, rowid AS rid from somedetails)
select tablea.id, tableb.details
from
tablea
left join tableb on tablea.rid = tableb.rid;
It is however as they have already warned you a really bad idea. What if the app breaks after inserting in one table but before the other one? What if you delete an old row? If you want to join two tables you need to specify the field to do so. There are so many things that could go wrong with this design. The most similar thing to this would be an incremental id field that you would save in the table and use in your application. Even simpler, make those into one table.
Read this link for more information about the rowid: https://www.sqlite.org/lang_createtable.html#rowid
sqlfiddle: http://sqlfiddle.com/#!7/29fd8/1
It is possible to use the rowid inside a with statement but you need to select it and make it available to the query using it. Something like this:
with tablea AS (select id, rowid AS rid from someids),
tableb AS (select details, rowid AS rid from somedetails)
select tablea.id, tableb.details
from
tablea
left join tableb on tablea.rid = tableb.rid;
The problem statement indicates:
The tables are ordered
If this means that the ordering is defined by the ordering of the values in the UNION ALL statements, and if SQLite respects that ordering, then the following solution may be of interest because, apart from small tweaks to the last three lines of the sample program, it adds just two lines:
A(rid,n) AS (SELECT ROW_NUMBER() OVER ( ORDER BY 1 ) rid, n FROM table_a),
B(rid,s) AS (SELECT ROW_NUMBER() OVER ( ORDER BY 1 ) rid, s FROM table_b)
That is, table A is table_a augmented with a rowid, and similarly for table B.
Unfortunately, there is a caveat, though it might just be the result of my not having found the relevant specifications. Before delving into that, however, here is the full proposed solution:
WITH
table_a (n) AS (
SELECT 2
UNION ALL
SELECT 4
UNION ALL
SELECT 5
),
table_b (s) AS (
SELECT 'valuex'
UNION ALL
SELECT 'valuey'
UNION ALL
SELECT 'valuez'
),
A(rid,n) AS (SELECT ROW_NUMBER() OVER ( ORDER BY 1 ) rid, n FROM table_a),
B(rid,s) AS (SELECT ROW_NUMBER() OVER ( ORDER BY 1 ) rid, s FROM table_b)
SELECT A.n, B.s
FROM A LEFT JOIN B
ON ( A.rid = B.rid );
Caveat
The proposed solution has been tested against a variety of data sets using sqlite version 3.29.0, but whether or not it is, and will continue to be, "guaranteed" to work is unclear to me.
Of course, if SQLite offers no guarantees with respect to the ordering of the UNION ALL statements (that is, if the question is based on an incorrect assumption), then it would be interesting to see a well-founded reformulation.

Find missing values in a sequence (sql)

Table1
Empid number
----------------
100 1
100 2
100 4
100 5
100 6
101 1
I'm self learning SQL, and a task I've come across is finding the missing values in sequence up to 12 and out putting which empid is associated.
I've attempted an approach that takes the above table and starts like
SELECT a number +1 , Min("through), MIn(by number) - 1
The entire approach use the existing numbers to find the missing "next/previous number. I'm able to output which numbers are missing. However I do not know how to group it with the associated id.
I also feel like I've complicated the task, I'm looking for guidance from anyone who can help on the best / most efficient way of going about this
Assuming that all empids and numbers are in the table somewhere, you can do this with a cross join and filter. In MS Access, this looks like:
select e.empid, n.number
from (select distinct empid from t) as e,
(select distinct number from t) as n
where not exists (select 1
from t
where t.empid = e.empid and t.number = n.number
);
This will not quite work for the data you have supplied. To handle that situation, you need a table that has the 12 numbers you are looking for.
Assumes you create a numbers table having Number column with 12 records value 1 to 12.
SELECT N.*, E.*
FROM NUMBERS N
CROSS JOIN (SELECT Distinct EmpID FROM table1) E
LEFT JOIN table1 T
on T.EmpID = E.EmpID
and T.Number = N.Number
WHERE T.EmpID is null
or substitute a derrived table for numbers table above
something like
(Select 1 as Number UNION ALL
Select 2 as Number UNION ALL
Select 3 as Number UNION ALL
Select 4 as Number UNION ALL
Select 5 as Number UNION ALL
Select 6 as Number UNION ALL
Select 7 as Number UNION ALL
Select 8 as Number UNION ALL
Select 9 as Number UNION ALL
Select 10 as Number UNION ALL
Select 11 as Number UNION ALL
Select 112 as Number)
I cant remember if MS Access will let you do this though...

How to create table with multiple rows and columns using only SELECT clause (i.e. using SELECT without FROM clause)

I know that in SQL Server, one can use SELECT clause without FROM clause, and create a table with one row and one column
SELECT 1 AS n;
But I was just wondering, is it possible to use SELECT clause without FROM clause, to create
a table with one column and multiple rows
a table with multiple columns and one row
a table with multiple columns and multiple rows
I have tried many combinations such as
SELECT VALUES(1, 2) AS tableName(n, m);
to no success.
You can do it with CTE and using union(Use union all if you want to display duplicates)
Rextester Sample for all 3 scenarios
One Column and multiple rows
with tbl1(id) as
(select 1 union all
select 2)
select * from tbl1;
One row and multiple columns
with tbl2(id,name) as
(select 1,'A')
select * from tbl2;
Multiple columns and multiple rows
with tbl3(id,name) as
(select 1,'A' union all
select 2,'B')
select * from tbl3;
-- One column, multiple rows.
select 1 as ColumnName union all select 2; -- Without FROM;
select * from ( values ( 1 ), ( 2 ) ) as Placeholder( ColumnName ); -- With FROM.
-- Multiple columns, one row.
select 1 as TheQuestion, 42 as TheAnswer; -- Without FROM.
select * from ( values ( 1, 42 ) ) as Placeholder( TheQuestion, TheAnswer ); -- With FROM.
-- Multiple columns and multiple rows.
select 1 as TheQuestion, 42 as TheAnswer union all select 1492, 12; -- Without FROM.
select * from ( values ( 1, 2 ), ( 2, 4 ) ) as Placeholder( Column1, Column2 ); -- With FROM.
You can do all that by using UNION keyword
create table tablename as select 1 as n,3 as m union select 2 as n,3 as m
In Oracle it will be dual:
create table tablename as select 1 as n,3 as m from dual union select 2 as n,3 as m from dual
You can use UNION operator:
CREATE TABLE AS SELECT column_name(s) FROM table1
UNION
SELECT column_name(s) FROM table2;
The UNION operator selects only distinct values by default. To allow duplicate values you can use UNION ALL.
The column names in the result-set are usually equal to the column names in the first SELECT statement in the UNION.
try this:
--1) a table with one column and multiple rows
select * into tmptable0 from(
select 'row1col1' as v1
union all
select 'row2col1' as v1
) tmp
--2) a table with multiple columns and one row
select 'row1col1' as v1, 'row1col2' as v2 into tmptable1
--3) a table with multiple columns and multiple rows
select * into tmptable2 from(
select 'row1col1' as v1, 'row1col2' as v2
union all
select 'row2col1' as v2, 'row2col2' as v2
) tmp
One can create a view and later can query it whenever required
-- table with one column and multiple rows
create view vw1 as
(
select 'anyvalue' as col1
union all
select 'anyvalue' as col1
)
select * from vw1
-- table with multiple columns and multiple rows
create view vw2 as
(
select 'anyvalue1' as col1, 'anyvalue1' as col2
union all
select 'anyvalue2' as col1, 'anyvalue2' as col2
)
select * from vw2

ORACLE join two table with comma separated ids

I have two tables
Table 1
ID NAME
1 Person1
2 Person2
3 Person3
Table 2
ID GROUP_ID
1 1
2 2,3
The IDs in all the columns above refer to the same ID (Example - a Department)
My Expected output (by joining both the tables)
GROUP_ID NAME
1 Person1
2,3 Person2,Person3
Is there a query with which I can achieve this.
It can be done. You shouldn't do it, but perhaps you don't have the power to change the world. (If you have a say in it, you should normalize your table design - in your case, both the input and the output fail the first normal form).
Answering more as good practice for myself... This solution guarantees that the names will be listed in the same order as the id's. It is not the most efficient, and it doesn't deal with id's in the list that are not found in the first table (it simply discards them instead of leaving a marker of some sort).
with
table_1 ( id, name ) as (
select 1, 'Person1' from dual union all
select 2, 'Person2' from dual union all
select 3, 'Person3' from dual
),
table_2 ( id, group_id ) as (
select 1, '1' from dual union all
select 2, '2,3' from dual
),
prep ( id, lvl, token ) as (
select id, level, regexp_substr(group_id, '[^,]', 1, level)
from table_2
connect by level <= regexp_count(group_id, ',') + 1
and prior id = id
and prior sys_guid() is not null
)
select p.id, listagg(t1.name, ',') within group (order by p.lvl) as group_names
from table_1 t1 inner join prep p on t1.id = p.token
group by p.id;
ID GROUP_NAMES
---- --------------------
1 Person1
2 Person2,Person3
select t2.group_id, listagg(t1.name,',') WITHIN GROUP (ORDER BY 1)
from table2 t2, table1 t1
where ','||t2.group_id||',' like '%,'||t1.id||',%'
group by t2.id, t2.group_id
Normalize you data model, this perversion !!! Сomma separated list should not exist in database. Only individual rows per data unit.