Use CTE to generate test data in SQL - sql

I am trying to use a CTE to generate a data table for a unit test in SQL (postgresql).
WITH temp1 AS (
SELECT
('A', 'A', 'B', 'B') AS grp,
(1, 2, NULL, 1) AS outcome
)
SELECT *
FROM temp1
The above query is generating a single row rather than a 4-row table that would be useful to my unit test. How can I generate the 4-row table in the form:
grp.....outcome
A.......1
A.......2
B.......NULL
B.......1

You could just use the values() syntax to create the rows, like so:
with temp1(grp, outcome) as (values ('A', 1), ('A', 2), ('B', null), ('B', 1))
select * from temp1
Demo on DB Fiddle:
grp | outcome
:-- | ------:
A | 1
A | 2
B | null
B | 1

You don't need a CTE. Use UNION ALL, as in:
select 'A' as grp, 1 as outcome
union all select 'A', 2
union all select 'B', null
union all select 'B', 1

WITH temp1 AS (
SELECT 'A' as grp ,1 as outcome
UNION
SELECT 'A', 2
UNION
SELECT 'B',NULL
UNION
SELECT 'B',1
) SELECT * FROM temp1

Related

SQL query to fetch distinct records

Can someone help me out with this sql query on postgres which I have to write but I just can't come up with, I have tried my best to simplify the problem from 1 million records and more constraints to this, I know this looks easy, but I am still unable to resolve this somehow :-
Table_name = t
Column_1_name = id
Column_2_name = st
Column_1_elements = [1,1,1,1,2,2,2,3,3]
Column_2_elements = [a,b,c,d,a,c,d,b,d]
Now I want to print to those distinct ids from id where they do not have their corresponding st equals to 'b' or 'a'.
For example, for the above example, the ouput should be [2,3] as 2 does not have corresponding 'b' and 3 does not have 'a'. [even though 3 does not have c also, but we are not concerned about 'c']. id=1 is not returned in solution as it has a relation with both 'a' and 'b'.
Let me know if you need more clarity.
Thanks in advance for helping.
edit1:- The number of elements for id = 1,2,3 could be anything. I just want those ids where there corresponding st does not "contain" 'a' or 'b'.
if there is an id=4 which has just one st which is 'r', and there is an id=5 which contains 'a','b','c','d','e','f','k','z'.
Then we want id=4 in the output as well as it does not contain 'a' or 'b'..
You might need to correct the syntax a little bit based on you SQL engine but this one is a working solution in Google BigQuery -
with temp as (
select 1 as id, 'a' as st union all
select 1 as id, 'b' as st union all
select 1 as id, 'c' as st union all
select 1 as id, 'd' as st union all
select 2 as id, 'a' as st union all
select 2 as id, 'c' as st union all
select 2 as id, 'd' as st union all
select 3 as id, 'b' as st union all
select 3 as id, 'd' as st union all
select 4 as id, 'e' as st union all
select 5 as id, 'g' as st union all
select 5 as id, 'h' as st
)
-- add 2 columns for is_a and is_b flags
, temp2 as (
select *
, case when st = 'a' then 1 else 0 end is_a
,case when st = 'b' then 1 else 0 end as is_b
from temp
)
-- IDs that have both the flags as 1 should be filtered out (like ID = 1)
select id
from temp2
group by 1
having max(is_a) + max(is_b) < 2
This solution takes care of the problem you mentioned with ID 4 . Let me know if this works for you.
See if this works:
create table t (id integer, st varchar);
insert into t values (1, 'a'), (1, 'b'), (1, 'c'), (1, 'd'), (2, 'a'), (2, 'c'), (2, 'd'), (3, 'b'), (3, 'd'), (4, 'r');
insert into t values (5, 'a'), (5, 'b'), (5, 'c'), (5, 'd'), (5, 'e'), (5, 'f'), (5, 'k'), (5, 'z');
select id, array['a', 'b'] <# array_agg(st)::text[] as tf from t group by id;
id | tf
----+----
3 | f
5 | t
4 | f
2 | f
1 | t
select * from (select id, array['a', 'b'] <# array_agg(st)::text[] as tf from t group by id) as agg where agg.tf = 'f';
id | tf
----+----
3 | f
4 | f
2 | f
In the first select query the array_agg(st) aggregates all the st values for an id via the group by id. array['a', 'b'] <# array_agg(st)::text[] then asks if the a and b are both in the array_agg.
The query is then turned into a sub-query where the outer query selects those rows that where 'f'(false), in other words did not have both a and b in the aggregated id values.

Fetch rows which has the same value in two columns for all the input values [duplicate]

This question already has answers here:
Oracle query to match all values in the list among all rows in table
(3 answers)
simple Oracle select statement syntax
(4 answers)
Closed 5 years ago.
I have a supplier table which has columns Item, suppliername and status. For the given items, I have to fetch the rows which has the same value in suppliername and status column only if the same values exists for all the given items.
For example, if the below is the table
Item Suppliername Status
A S1 Created
A S1 Approved
B S1 Approved
B S2 Created
C S1 Created
C S1 Approved
Input given are Items 'A', 'B', 'C'
The output should be as below.
Suppliername Status
S1 Approved
A few options:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TYPE CharList IS TABLE OF CHAR(1)
/
CREATE TABLE table_name ( Item, Suppliername, Status ) AS
SELECT 'A', 'S1', 'Created' FROM DUAL UNION ALL
SELECT 'A', 'S1', 'Approved' FROM DUAL UNION ALL
SELECT 'B', 'S1', 'Approved' FROM DUAL UNION ALL
SELECT 'B', 'S2', 'Created' FROM DUAL UNION ALL
SELECT 'C', 'S1', 'Created' FROM DUAL UNION ALL
SELECT 'C', 'S1', 'Approved' FROM DUAL
/
Query 1:
SELECT Suppliername, Status
FROM table_name
GROUP BY Suppliername, Status
HAVING CharList( 'A', 'B', 'C' )
SUBMULTISET OF CAST( COLLECT( Item ) AS CharList )
Results:
| SUPPLIERNAME | STATUS |
|--------------|----------|
| S1 | Approved |
Query 2:
SELECT Suppliername, Status
FROM table_name
WHERE Item IN ( 'A', 'B', 'C' )
GROUP BY Suppliername, Status
HAVING COUNT( DISTINCT item ) = 3
Results:
| SUPPLIERNAME | STATUS |
|--------------|----------|
| S1 | Approved |
Query 3:
SELECT Suppliername, Status
FROM table_name
WHERE Item MEMBER OF CharList( 'A', 'B', 'C' )
GROUP BY Suppliername, Status
HAVING COUNT( DISTINCT item ) = CARDINALITY( CharList( 'A', 'B', 'C' ) )
Results:
| SUPPLIERNAME | STATUS |
|--------------|----------|
| S1 | Approved |
This is how I would do it:
WITH picklist(Item) AS
(
VALUES ('A'),
('B'),
('C')
), grouping AS
(
SELECT T.Suppliername, T.Status, COUNT(*) AS C
FROM TABLE T
JOIN picklist ON T.Item = picklist.Item
GROUP BY T.Suppliername, T.Status
)
SELECT Suppliername, Status
FROM grouping
WHERE C = (SELECT COUNT(*) FROM picklist)
If Values does not work on your platform you can use for SQL Server
SELECT 'A' as Item
UNION ALL
SELECT 'B' as Item
UNION ALL
SELECT 'C' as Item
and For Oracle (according to MT0)
SELECT 'A' as Item FROM DUAL
UNION ALL
SELECT 'B' as Item FROM DUAL
UNION ALL
SELECT 'C' as Item FROM DUAL

What Microsoft tSql code will create "Group Identifiers" for groups of rows delimited by a 1

--drop table GroupIdentifierTest
go
Create table GroupIdentifierTest
(
RowId varchar(10),
[Delimiter] int
)
insert into GroupIdentifierTest
select 'a', 0 union all
select 'b', 0 union all
select 'c', 0 union all
select 'd', 1 union all
select 'e', 0 union all
select 'f', 1
select * from GroupIdentifierTest
delete GroupIdentifierTest
insert into GroupIdentifierTest
select 'a', 1 union all
select 'b', 1 union all
select 'c', 1 union all
select 'd', 2 union all
select 'e', 2 union all
select 'f', 2
select r.RowId, r.Delimiter As GroupId from GroupIdentifierTest r
If you have a rowset, and the data is in groupings, indicated by a column value of 1, how can you generate GroupId numbers?
In this example, the first rowset needs to be transformed into the second rowset.
How many groups do you need? Perhaps this will help:
WITH prep AS
(
SELECT RowId, delimiter, GroupId = NTILE(2) OVER (ORDER BY RowID)
FROM GroupIdentifierTest
)
UPDATE prep
SET delimiter = GroupId;
SELECT RowId, Delimiter AS groupID
FROM GroupIdentifierTest;
Results:
RowId groupID
---------- -----------
a 1
b 1
c 1
d 2
e 2
f 2

How to create a sequential range in SQL?

I have a table where each row has a From and a To:
From|To
A | B
C | D
B | C
D | E
And I need to order them in a sequence where "To" in the first row is the "From" of the following and so on.
The result should be ordered like this:
From|To
A| B
B| C
C| D
D| E
The main problem is finding the From that follows the To in the previous record.
Not sure if I got your question right. Why don't you just sort?
;with cte as
(
select 'B' as "From", 'C' as "To"
union
select 'A', 'B'
union
select 'C', 'D'
)
select "From", "To"
from cte
order by "From";
(You don't need the cte/union stuff in your query, it's just for the sake of sample data.)
If you want to list only entries with successors:
;with cte as
(
select 'B' as "From", 'C' as "To"
union
select 'A', 'B'
union
select 'C', 'D'
union
select 'F', 'G'
)
select "From", "To"
from cte
where exists(select *
from cte cte2
where cte2."From" = cte."To")
or exists(select *
from cte cte3
where cte3."To" = cte."From")
order by "From";
Btw: Try using other column names than "From" and "To" as they are reserved statements that have to be used with "" (ANSI) or [] (T SQL).
declare #v table (ffrom varchar(10), fto varchar(10));
insert into #v values
('e', 'f'),
('a', 'b'),
('g', 'h'),
('c', 'd');
select *
from #v
order by ffrom;
GO
ffrom | fto
:---- | :--
a | b
c | d
e | f
g | h
dbfiddle here
You can use a recursive CTE:
with cte as (
select from, to, 1 as lev
from t
where not exists (select 1 from t t2 where t2.to = t.from)
union all
select t.from, t.to, cte.lev + 1
from cte join
t
on cte.to = t.from
)
select to, from
from cte
order by lev, to;
Note that this is affected by the maximum recursion depth, but it will work fine on four rows.

Update Oracle SQL - table with values from duplicates

i hope that somebody could help. I need to update a table from a select with duplicates.
ID;CLASS;VALUE;NEW
1;a;a3;
1;b;s6;
1;c;b99;
2;a;s3;
2;b;r6;
2;c;b99;
3;a;s5;
4;a;r6;
4;b;a3;
Look at my example table, there is a colum NEW which i have to update. In the example the column NEW was filled manually.
Here is the goal (as shown in table col NEW):
1.find duplicates via ID (HAVING COUNT(*) >1 or something like that)
UPDATE TABLE SET NEW=
CLASS || '_' || VALUE
WHERE CLASS='a' or 'b'
Easy for you?
Thx in advance
The logic behind is not completely clear; this could be a way.
setup:
create table yourTable(id, class, value, new) as
(
select 1, 'a', 'a3', cast (null as varchar2(10)) from dual union all
select 1, 'b', 's6', null from dual union all
select 1, 'c', 'b99', null from dual union all
select 2, 'a', 's3', null from dual union all
select 2, 'b', 'r6', null from dual union all
select 2, 'c', 'b99', null from dual union all
select 3, 'a', 's5', null from dual union all
select 4, 'a', 'r6', null from dual union all
select 4, 'b', 'a3', null from dual
)
query:
merge into yourTable t1
using (
select listagg(value, '_') within group (order by class) as new,
id
from yourTable
where class in ('a', 'b')
group by id
having count(distinct class) = 2
) t2
on ( t1.id = t2.id
and t1.class in ('a', 'b')
)
when matched then
update set t1.new = t2.new
result:
SQL> select *
2 from yourTable;
ID C VAL NEW
---------- - --- ----------
1 a a3 a3_s6
1 b s6 a3_s6
1 c b99
2 a s3 s3_r6
2 b r6 s3_r6
2 c b99
3 a s5
4 a r6 r6_a3
4 b a3 r6_a3
9 rows selected.