How to duplicate each row in sql query? - sql

Suppose we have a query
SELECT * FROM my_table ORDER BY id
which results in
id | title
-----------
1 | 'ABC'
2 | 'DEF'
3 | 'GHI'
How could I modify given select statement to have each row duplicated in the result set like this:
id | title
-----------
1 | 'ABC'
1 | 'ABC'
2 | 'DEF'
2 | 'DEF'
3 | 'GHI'
3 | 'GHI'

Try this...
SELECT * FROM my_table
UNION ALL
SELECT * FROM my_table
ORDER BY id

You can use union all, but I like using cross join:
select *
from MyTable cross join
(select 1 from dual union all select 2 from dual) n
order by id;
The reason I like the cross join is in the case where MyTable is really some complicated subquery. Although the query optimizer might evaluate it only once, you can't really depend on that fact. So the performance should be better in this case.

You could cross join to a row generator, the numeric value indicates how many duplicates per original you want.
select *
from my_table
cross join
(select null
from dual
connect by level <= 2)
order by id

Related

Need to get the mismatcted cells in specific partition

ID
TC_No
Result
1
tc_1
PASS
1
tc_2
PASS
1
tc_3
FAIL
1
tc_4
PASS
1
tc_5
FAIL
2
tc_1
FAIL
2
tc_2
PASS
2
tc_3
FAIL
2
tc_4
FAIL
2
tc_5
FAIL
I'm trying to find all records that have conflicting "Result" on the same "TC_No" and among different "ID" values, filtered by ID IN (1,2).
Here's the expected output:
ID
TC_No
Result
1
tc_1
PASS
1
tc_4
PASS
2
tc_1
FAIL
2
tc_4
FAIL
and my attempted query:
SELECT * From
(SELECT * from Excel As T1
UNION
SELECT * from Excel As T2)
As c
where ID in(1,2) order By TC_NO
find the distinct count of result for each tc_no and then select the records having count greater than 1
Query
select * from your_tbl_name a
where exists(
select 1 from (
select tc_no, count(distinct result) as cnt
from your_tbl_name
where result in ('PASS','FAIL')
group by c_no
) b
where a.tc_no = b.tc_no
and b.cnt > 1
)
You can simply INNER JOIN the table onto itself and add a predicate in the WHERE clause to return only mismatched results.
SQL:
SELECT
a.ID,
a.TC_No,
a.Result
FROM
Excel a
INNER JOIN Excel b ON a.TC_No = b.TC_No
WHERE
a.Result <> b.Result;
Result:
| ID | TC_No | Result |
|----|-------|--------|
| 1 | tc_1 | PASS |
| 1 | tc_4 | PASS |
| 2 | tc_1 | FAIL |
| 2 | tc_4 | FAIL |
SQL Fiddle Demo:
Here
Check when the maximum result is different than the minimum one, in your filtered data, using window functions.
WITH cte AS (
SELECT tab.*,
MAX(Result_) OVER(PARTITION BY TC_No) AS max_result,
MIN(Result_) OVER(PARTITION BY TC_No) AS min_result
FROM tab
WHERE ID IN (1,2)
)
SELECT Id, Tc_No, Result_
FROM cte
WHERE min_result < max_result
Check the demo here.
You could use analytic function COUNT() OVER() to find the rows with different content and then just filter the result in Where clause:
SELECT ID, TC_NO, RESULT
FROM ( Select ID, TC_NO, RESULT,
CASE WHEN Count(DISTINCT RESULT) OVER(Partition By TC_NO) = 2 THEN 'Y' END "IS_DIFF"
From tbl
)
WHERE IS_DIFF = 'Y'
ORDER BY ID
With your sample data:
WITH
tbl (ID, TC_NO, RESULT) AS
(
Select 1, 'tc_1', 'PASS' From Dual Union All
Select 1, 'tc_2', 'PASS' From Dual Union All
Select 1, 'tc_3', 'FAIL' From Dual Union All
Select 1, 'tc_4', 'PASS' From Dual Union All
Select 1, 'tc_5', 'FAIL' From Dual Union All
Select 2, 'tc_1', 'FAIL' From Dual Union All
Select 2, 'tc_2', 'PASS' From Dual Union All
Select 2, 'tc_3', 'FAIL' From Dual Union All
Select 2, 'tc_4', 'FAIL' From Dual Union All
Select 2, 'tc_5', 'FAIL' From Dual
)
... the result is:
ID
TC_NO
RESULT
1
tc_1
PASS
1
tc_4
PASS
2
tc_1
FAIL
2
tc_4
FAIL

How can I count the total no of rows which is not containing some value in array field? It should include null values as well

| names |
| -----------------------------------|
| null |
| null |
| [{name:'test'},{name:'test1'}] |
| [{name:'test'},{name:'test1'}] |
| [{name:'test1'},{name:'test2'}] |
I want to count the no of rows which does not have the value 'test' in the name key.
Here it should give answer as 3 (Row no 1, 2 and 5th row) because all these row do not contain the value 'test'.
Use below approach
select count(*)
from your_table
where 0 = ifnull(array_length(regexp_extract_all(names, r"\b(name:'test')")), 0)
you can test it with below data (that resemble whatever you presented in your question)
with your_table as (
select null names union all
select null union all
select "[{name:'test'},{name:'test1'}]" union all
select "[{name:'test'},{name:'test1'}]" union all
select "[{name:'test1'},{name:'test2'}]"
)
with output
Below approach will work,
with your_table as (
select null names union all
select null union all
select [('name','test'),('name','test1')] union all
select [('name','test'),('name','test1')] union all
select [('name','test1'),('name','test2')]
)
SELECT COUNT(*) as count FROM your_table
WHERE NOT EXISTS (
SELECT 1
FROM UNNEST(your_table.names) AS names
WHERE names IN (('name','test'))
)

How to select a single row multiple times in PostgreSql

I want to print 4 times the same row in PostgreSQL, how to achieve that ?
Table : mytable
Id | name
------------
1 | foo
2 | bar
3 | zzz
I want something like
Select 4x mytable.* from mytable where id=1
And the result should be
Id | name
------------
1 | foo
1 | foo
1 | foo
1 | foo
You can cross join against generate_series(1,4), which will return a table containing the numbers 1 to 4:
SELECT mytable.*
FROM mytable
CROSS JOIN generate_series(1,4) as x
WHERE id=1
For each row in your original result set, there will be one copy with 1 next to it, one with 2, and so on.
you can use generate_series.
sample:
t=# create table so48 (i int,n text);
CREATE TABLE
t=# insert into so48 select 1,'a';
INSERT 0 1
t=# insert into so48 select 2,'b';
INSERT 0 1
select:
t=# with s as (select generate_series(1,4,1) g) select so48.* from so48 join s on true where i = 1;
i | n
---+---
1 | a
1 | a
1 | a
1 | a
(4 rows)
use union all
Select mytable.* from mytable where id=1
union all Select mytable.* from mytable where id=1
union all Select mytable.* from mytable where id=1
union all Select mytable.* from mytable where id=1
Cross join should do the job
Select 4x mytable.* from mytable where id=1
cross join
(select 1 from dual union all
select 1 from dual union all
select 1 from dual union all
select 1 from dual )

oracle SQL select/join with many-to-one and nulls

I'm having trouble crafting an Oracle SQL query that returns the results I seek. It's possible that what I'm trying to do isn't possible.
For a given code in table one, if the code exists in table two and ANY of the flags are "1", then the status should be "1" in the query results. Otherwise, the status should be "0". If the code doesn't exist at all in table 2, then the status should be null.
tab1
------------
id,code
------------
1,ABC
2,DEF
3,GHI
4,JKL
5,MNO
6,PQR
7,STU
tab2
------------
id,code,flag
------------
1,ABC,0
2,ABC,0
3,DEF,1
4,DEF,1
5,GHI,0
6,GHI,1
7,JKL,1
8,JKL,0
9,MNO,0
10,PQR,1
(query?)
result
------------
id,code,status
------------
1,ABC,0
2,DEF,1
3,GHI,1
4,JKL,1
5,MNO,0
6,PQR,1
7,STU,null
So far, the only query I've been able to come up with is this, which doesn't give the right results in the status column...
select tab1.*, (select * from (
select flag from tab2 where tab2.code = code order by flag desc)
where rownum <=1) as status from tab1;
... status is always "1", which is incorrect.
I'm thinking that instead of using order by and selecting the first result, it might be possible to instead count the number of "1" flags for each code, but I'm not sure if that would work.
My first inclination is to use a subselect:
select t1.*,
(select max(t2.flag)
from table2 t2
where t2.code = t1.code
) as t2flag
from table1 t1;
You can also phrase this as a left join with an aggregation:
select t1.*, t2.flag
(select max(t2.flag)
from table2 t2
where t2.code = t1.code
) as t2flag
from table1 t1 left join
(select t2.code, max(t2.flag) as flag
from table2 t2
group by t2.code
) t2
on t2.code = t1.code;
Both these methods are assuming that flag is either 0 or 1, as in your question.
I'd use a LEFT JOIN:
select t1.id, max(t1.code) code, max(t2.flag) status
from table1 t1
left join table2 t2 on t1.code=t2.code
group by t1.id
Assuming that 0 and 1 are the only two flags, what about something like:
with tab1 as (select 1 id, 'ABC' code from dual union all
select 2 id, 'DEF' code from dual union all
select 3 id, 'GHI' code from dual union all
select 4 id, 'JKL' code from dual union all
select 5 id, 'MNO' code from dual union all
select 6 id, 'PQR' code from dual union all
select 7 id, 'STU' code from dual),
tab2 as (select 1 id, 'ABC' code, 0 flag from dual union all
select 2 id, 'ABC' code, 0 flag from dual union all
select 3 id, 'DEF' code, 1 flag from dual union all
select 4 id, 'DEF' code, 1 flag from dual union all
select 5 id, 'GHI' code, 0 flag from dual union all
select 6 id, 'GHI' code, 1 flag from dual union all
select 7 id, 'JKL' code, 1 flag from dual union all
select 8 id, 'JKL' code, 0 flag from dual union all
select 9 id, 'MNO' code, 0 flag from dual union all
select 10 id, 'PQR' code, 1 flag from dual)
select t1.id,
t1.code,
t2.flag status
from tab1 t1
left join (select code,
max(flag) flag
from tab2
group by code) t2
on (t1.code = t2.code);
ID CODE STATUS
---------- ---- ----------
5 MNO 0
6 PQR 1
2 DEF 1
1 ABC 0
3 GHI 1
4 JKL 1
7 STU
Maybe I'm missing something, but it seems to be as simple as this:
select tab1.id, tab1.code, max(tab2.flag) status
from tab1
left join tab2 on tab1.code = tab2.code
group by tab1.id, tab1.code
order by tab1.id;
A sample SQL Fiddle gives the desired result:
| id | code | status |
|----|------|--------|
| 1 | ABC | 0 |
| 2 | DEF | 1 |
| 3 | GHI | 1 |
| 4 | JKL | 1 |
| 5 | MNO | 0 |
| 6 | PQR | 1 |
| 7 | STU | (null) |
SELECT ID,
CODE,
MAX( STATUS)
FROM
(SELECT DISTINCT T1.ID,
T1.CODE,
FLAG AS STATUS
FROM T1
LEFT JOIN T2
ON T1.CODE = T2.CODE
)
GROUP BY ID,
CODE;
This gives result as you asked:
SELECT tab1.id,tab1.code,max(tab2.flag) as Status
FROM tab1 LEFT JOIN tab2
ON tab1.code=tab2.code
GROUP BY tab2.code
ORDER BY tab1.id;

sql range operator

Is there a SQL construct or a trick to do something like:
SELECT i WHERE i BETWEEN 0 AND 10;
?
My first idea is to create a temporary table such as:
CREATE TABLE _range ( i INT PRIMARY KEY );
and fill it
INSERT INTO _range VALUES 0;
INSERT INTO _range VALUES 1;
etc.
Is there a better way?
UPDATE:
I use sqlite in this particular case but i am interested in general answer.
What DB are you using? The between operator generally is legal syntax for SQL. We use it all the time for date ranges.
from http://www.w3schools.com/Sql/sql_between.asp
SELECT column_name(s)
FROM table_name
WHERE column_name
BETWEEN value1 AND value2
Very interesting question. Here's an ugly hack that probably is useless unless your ranges are really that small...
select * from (
select 1 as num UNION
select 2 as num UNION
select 3 as num UNION
select 4 as num UNION
select 5 as num UNION
select 6 as num UNION
select 7 as num UNION
select 8 as num UNION
select 9 as num UNION
select 10 as num
) t ;
+-----+
| num |
+-----+
| 1 |
| 2 |
....
| 9 |
| 10 |
+-----+
10 rows in set (0.00 sec)
Edit: Ok, so I got to thinking why not use cross joins. So, here is another hack and this one will quickly net you pretty large in memory tables and is perhaps reasonably good.
select POW(2,0)*t0.num + POW(2,1)*t1.num + POW(2,2)*t2.num + POW(2,3)*t3.num
as num
from (
select 0 as num UNION
select 1 as num
) t0, (
select 0 as num UNION
select 1 as num
) t1, (
select 0 as num UNION
select 1 as num
) t2, (
select 0 as num UNION
select 1 as num
) t3
order by num ;
+------+
| num |
+------+
| 0 |
| 1 |
....
| 14 |
| 15 |
+------+
16 rows in set (0.00 sec)
Will easily go to any power of 2 and should be fast enough.
with MS SQL you could use CTE :
;with numbs as
(
select 1 as col
union all
select col + 1 from numbs
where col < 10
)
select * from numbs
An SQL syntax that multiply records is CROSS JOIN
I am not sure from your request and comments, but I guess you are trying to do that.
I have done a similar thing to populate a table with a datestamp as string "YYYYMM" for a period of 20 years.
You did not specify the dbms you use, but with Oracle you could use CONNECT BY:
SELECT ROWNUM-1 i
FROM dual
CONNECT BY ROWNUM <= 10 + 1