SQL count distinct empty values Oracle / Redshift - sql

I'm having different results between Oracle and Redshift when I do a count(distinct my_field).
Assuming my_field has the following values : "", a, b c.
Oracle's count distinct will give me 3.
Redshift's count distinct will give me 4 (unless I specifically add a clause testing length > 0).
Has anyone seen this before ?
Is there a way to set up the database so it ignores empty values in a distinct count ?
Thanks a lot !

## CREATE TABLE
dev=# create table my_table (my_key integer null, my_field varchar null) diststyle even;
CREATE TABLE
## LOAD DATA
dev=# insert into my_table values (0, 'A');
INSERT 0 1
dev=# insert into my_table values (1, 'B');
INSERT 0 1
dev=# insert into my_table values (2, 'C');
INSERT 0 1
dev=# insert into my_table values (3, NULL);
INSERT 0 1
dev=# insert into my_table values ( NULL, 'E');
INSERT 0 1
dev=# insert into my_table values (6, NULL);
INSERT 0 1
dev=# insert into my_table values (7, NULL);
INSERT 0 1
dev=# insert into my_table values (8, 'A');
INSERT 0 1
##CHECK CONTENTS OF TABLE
dev=# SELECT * FROM my_table;
my_key | my_field
--------+----------
0 | A
2 | C
3 |
| E
1 | B
6 |
7 |
8 | A
(8 rows)
## DISTINCT RESULTS
dev=# SELECT DISTINCT my_field FROM my_table;
my_field
----------
C
E
B
A
(5 rows)
dev=# SELECT COALESCE(my_field, 'NULL') FROM my_table GROUP BY 1 ;
coalesce
----------
A
NULL
C
E
B
(5 rows)
dev=# SELECT DISTINCT COALESCE(my_field, 'NULL') FROM my_table WHERE my_field ;
ERROR: argument of WHERE must be type boolean, not type character varying
dev=# SELECT DISTINCT my_field FROM my_table WHERE my_field IS NOT NULL;
my_field
----------
B
C
E
A
(4 rows)
dev=#

Related

How to replace a value in a comma separated string column in Oracle

I want to replace all the occurrences of 4 with the number 2 in a string column of a table.
This is a hardcoded value of 2 which replaces all occurrences of the number 4 in a Oracle table. The LOBS column is a VARCHAR column
ID
LOBS
1
1,4,6,7,8
2
1,5,6,7,9,4
3
3,5,7,8,11,4
New Table
ID
LOBS
1
1,2,6,7,8
2
1,5,6,7,9,2
3
3,5,7,8,11,2
In Oracle, you can use simple (quick) string functions:
SELECT id,
TRIM(BOTH ',' FROM REPLACE(','||lobs||',', ',4,', ',2,'))
AS updated_lobs
FROM table_name;
Or (slower) regular expressions:
SELECT id,
REGEXP_REPLACE(lobs,'(^|,)4(,|$)','\12\2') AS updated_lobs
FROM table_name
Which, for the sample data:
CREATE TABLE table_name (ID, LOBS) AS
SELECT 1, '1,4,64,7,8' FROM DUAL UNION ALL
SELECT 2, '4,1,5,64,7,9' FROM DUAL UNION ALL
SELECT 3, '3,5,64,8,11,4' FROM DUAL;
Both output:
ID
UPDATED_LOBS
1
1,2,64,7,8
2
2,1,5,64,7,9
3
3,5,64,8,11,2
db<>fiddle here
It seems a simple REPLACE function Oracle will work for you -
SELECT REPLACE(LOBS, '4', '2')
FROM your_table;
You could use REGEP_REPLACE
CREATE TABLE table1 (
ID INTEGER,
LOBS VARCHAR2(12)
);
INSERT INTO table1
(ID, LOBS)
VALUES
('1', '1,2,6,7,8');
INSERT INTO table1
(ID, LOBS)
VALUES
('2', '2,6,7,8,9');
INSERT INTO table1
(ID, LOBS)
VALUES
('3', '1,5,6,7,9,2');
INSERT INTO table1
(ID, LOBS)
VALUES
('4', '3,5,7,8,11,2');
SELECT REGEXP_REPLACE(
REGEXP_REPLACE(
REGEXP_REPLACE(LOBS,'^2,','4,')
,'(,2,)',',4,'),',2$',',4') FROM table1
WHERE 1= 1
| REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(LOBS,'^2,','4,'),'(,2,)',',4,'),',2$',',4') |
| :--------------------------------------------------------------------------------------- |
| 1,4,6,7,8 |
| 4,6,7,8,9 |
| 1,5,6,7,9,4 |
| 3,5,7,8,11,4 |
db<>fiddle here

TSQL Make partitions "gaps and island"

I need to create partitions. Suppose I have this table:
CREATE TABLE MyTable (Pos INT UNIQUE, X INT)
INSERT INTO MyTable VALUES (3, 2)
INSERT INTO MyTable VALUES (5, 0)
INSERT INTO MyTable VALUES (6, 0)
INSERT INTO MyTable VALUES (9, 0)
INSERT INTO MyTable VALUES (43, 9)
INSERT INTO MyTable VALUES (53, 8)
INSERT INTO MyTable VALUES (56, 0)
INSERT INTO MyTable VALUES (81, 0)
INSERT INTO MyTable VALUES (163, 1)
INSERT INTO MyTable VALUES (9716, 0)
The query result should be this table with a column Y added, Y should be
IF X=0 : the previous value X<>0 (OR NULL, if not exists), ordered by Pos
IF X<>0 : X
Desired answer table looks like this
SELECT *
FROM MyQuery as a function of MyTable
ORDER BY Pos
Pos X Y
3 2 2
5 0 2
6 0 2
9 0 2
43 9 9
53 8 8
56 0 8
81 0 8
163 1 1
9716 0 1
This is a type of gaps-and-islands problem.
There are many solutions, here is one:
Use a running conditional count to number the rows that we want to group together
Use a partitioned conditional MIN to take the only value that we actually want, per group
WITH StartPoints AS (
SELECT *,
GroupId = COUNT(NULLIF(X, 0)) OVER (ORDER BY Pos)
FROM MyTable
)
SELECT
Pos,
X,
Y = MIN(NULLIF(X, 0)) OVER (PARTITION BY GroupId)
FROM StartPoints
ORDER BY Pos;
db<>fiddle

Update OrderID based on the Select Order By Results?

I am using SQLite 3. I have a table MyTable, as follows:
Create table mytable (ID as INTEGER, OrderID as INTEGER, a as INTGER, b as INTEGER);
Insert into mytable (ID, OrderID,a,b) values (1, 1,1,1);
Insert into mytable (ID, OrderID,a,b) values (1, 2,1,2);
Insert into mytable (ID, OrderID,a,b) values (2, 1,1,3);
Insert into mytable (ID, OrderID,a,b) values (2, 3,2,1);
Insert into mytable (ID, OrderID,a,b) values (3, 1,2,3);
Now if I using the following statement:
Select * from mytable ORDER BY a desc, b desc;
I will get all rows in a different order, as follows:
(3, 1, 2, 3);
(2, 3, 2, 1);
(2, 1, 1, 3);
(1, 2, 1, 2);
(1, 1, 1, 1);
Now I want to update the order ID to the sequence number of the rows appear in the above results, as follows:
(3, 1, 2, 3);
(2, 2, 2, 1);
(2, 3, 1, 3);
(1, 4, 1, 2);
(1, 5, 1, 1);
How to do so?
Try This :-
Select ROW_NUMBER() OVER (ORDER BY (SELECT 1 ) ) AS ordNo, * INTO #TempTable from
mytable ORDER BY a desc, b desc;
UPDATE mytable SET OrderID = ordNo FROM #TempTable WHERE mytable.ID
=#TempTable.ID AND mytable.a=#TempTable.a AND mytable.b=#TempTable.b
Assuming that the a and b values are unique:
update mytable t
set orderid = (select count(*)
from mytable t2
where t2.a > t.a or
(t2.a = t.a and t2.b >= t.b)
);
There is a column [rowid][1] responsible for every rowid
Most tables in a typical SQLite database schema are rowid tables.
Rowid tables are distinguished by the fact that they all have a unique, non-NULL, signed 64-bit integer rowid that is used as the access key for the data in the underlying B-tree storage engine.
Due to old SQLite version didn't support ROW_NUMBER window function, you can use a subquery in select to make it.
You can try to use correlated subquery and UPDATE by rowid.
Schema (SQLite v3.18)
Create table mytable (ID INT, OrderID INT, a INT, b INT);
Insert into mytable (ID, OrderID,a,b) values (1, 1,1,1);
Insert into mytable (ID, OrderID,a,b) values (1, 2,1,2);
Insert into mytable (ID, OrderID,a,b) values (2, 1,1,3);
Insert into mytable (ID, OrderID,a,b) values (2, 3,2,1);
Insert into mytable (ID, OrderID,a,b) values (3, 1,2,3);
update mytable
set orderid=
(
SELECT (select count(*)
from mytable tt
where tt.a > t1.a or
(tt.a = t1.a and tt.b >= t1.b)) rn
FROM mytable t1
where mytable.rowid=t1.rowid
);
Query #1
SELECT * FROM mytable order by OrderID;
| ID | OrderID | a | b |
| --- | ------- | --- | --- |
| 3 | 1 | 2 | 3 |
| 2 | 2 | 2 | 1 |
| 2 | 3 | 1 | 3 |
| 1 | 4 | 1 | 2 |
| 1 | 5 | 1 | 1 |
View on DB Fiddle

Stick two tables together

Let's consider two tables:
First:
Id Data
1 asd
2 buu
And Second:
UPD:
Id Data
10 ffu
11 fffuuu
10001 asd
I want to get a 4-column table looking like this:
Id1 Data1 Id2 Data2
1 asd 10 fuu
2 buu 11 fffuuu
-1 [any text] 10001 asd
(if the numbers of rows are not equal ,let's use "-1" for the id)
How to do this?
I'm using sqlite3-3.7.3.
UPD2:
There is no matching criteria between tables,any random matching between them will be sufficient for me.
Assuming that the id columns are unique and not null, you can "zip" your tables by:
Creating a row number for each row that corresponds to the
position of the row when the table is ordered by the unique id (as
polishchuk mentioned in his comment); and,
Simulating a FULL OUTER JOIN with 2 LEFT OUTER JOINS.
To demonstrate, I used two tables with differing row counts:
CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT, data TEXT);
INSERT INTO foo VALUES (NULL, 'a');
INSERT INTO foo VALUES (NULL, 'b');
INSERT INTO foo VALUES (NULL, 'c');
INSERT INTO foo VALUES (NULL, 'd');
INSERT INTO foo VALUES (NULL, 'e');
INSERT INTO foo VALUES (NULL, 'f');
INSERT INTO foo VALUES (NULL, 'g');
INSERT INTO foo VALUES (NULL, 'h');
INSERT INTO foo VALUES (NULL, 'i');
INSERT INTO foo VALUES (NULL, 'j');
DELETE FROM foo WHERE data IN ('b', 'd', 'f', 'i');
CREATE TABLE bar (id INTEGER PRIMARY KEY AUTOINCREMENT, data TEXT);
INSERT INTO bar VALUES (NULL, 'a');
INSERT INTO bar VALUES (NULL, 'b');
INSERT INTO bar VALUES (NULL, 'c');
INSERT INTO bar VALUES (NULL, 'd');
INSERT INTO bar VALUES (NULL, 'e');
INSERT INTO bar VALUES (NULL, 'f');
INSERT INTO bar VALUES (NULL, 'g');
INSERT INTO bar VALUES (NULL, 'h');
INSERT INTO bar VALUES (NULL, 'i');
INSERT INTO bar VALUES (NULL, 'j');
DELETE FROM bar WHERE data IN ('a', 'b');
To obtain a more readable output, I then ran:
.headers on
.mode column
Then you can execute this SQL statement:
SELECT COALESCE(id1, -1) AS id1, data1,
COALESCE(id2, -1) as id2, data2
FROM (
SELECT ltable.rnum AS rnum,
ltable.id AS id1, ltable.data AS data1,
rtable.id AS id2, rtable.data AS data2
FROM
(SElECT (SELECT COUNT(*) FROM foo
WHERE id <= T1.id) rnum, id, data FROM foo T1
) ltable
LEFT OUTER JOIN
(SElECT (SELECT COUNT(*) FROM bar
WHERE id <= T1.id) rnum, id, data FROM bar T1
) rtable
ON ltable.rnum=rtable.rnum
UNION
SELECT rtable.rnum AS rnum,
ltable.id AS id1, ltable.data AS data1,
rtable.id AS id2, rtable.data AS data2
FROM
(SElECT (SELECT COUNT(*) FROM bar
WHERE id <= T1.id) rnum, id, data FROM bar T1
) rtable
LEFT OUTER JOIN
(SElECT (SELECT COUNT(*) FROM foo
WHERE id <= T1.id) rnum, id, data FROM foo T1
) ltable
ON ltable.rnum=rtable.rnum)
ORDER BY rnum
Which gives you:
id1 data1 id2 data2
---------- ---------- ---------- ----------
1 a 3 c
3 c 4 d
5 e 5 e
7 g 6 f
8 h 7 g
10 j 8 h
-1 9 i
-1 10 j
This works "both ways", for example, if you invert the two tables (foo and bar), you get:
id1 data1 id2 data2
---------- ---------- ---------- ----------
3 c 1 a
4 d 3 c
5 e 5 e
6 f 7 g
7 g 8 h
8 h 10 j
9 i -1
10 j -1

double sorted selection from a single table

I have a table with an id as the primary key, and a description as another field.
I want to first select the records that have the id<=4, sorted by description, then I want all the other records (id>4), sorted by description. Can't get there!
select id, descr
from t
order by
case when id <= 4 then 0 else 1 end,
descr
select *, id<=4 as low from table order by low, description
You may want to use an id <= 4 expression in your ORDER BY clause:
SELECT * FROM your_table ORDER BY id <= 4 DESC, description;
Test case (using MySQL):
CREATE TABLE your_table (id int, description varchar(50));
INSERT INTO your_table VALUES (1, 'c');
INSERT INTO your_table VALUES (2, 'a');
INSERT INTO your_table VALUES (3, 'z');
INSERT INTO your_table VALUES (4, 'b');
INSERT INTO your_table VALUES (5, 'g');
INSERT INTO your_table VALUES (6, 'o');
INSERT INTO your_table VALUES (7, 'c');
INSERT INTO your_table VALUES (8, 'p');
Result:
+------+-------------+
| id | description |
+------+-------------+
| 2 | a |
| 4 | b |
| 1 | c |
| 3 | z |
| 7 | c |
| 5 | g |
| 6 | o |
| 8 | p |
+------+-------------+
8 rows in set (0.00 sec)
Related post:
Using MySql, can I sort a column but have 0 come last?
select id, description
from MyTable
order by case when id <= 4 then 0 else 1 end, description
You can use UNION
SELECT * FROM (SELECT * FROM table1 WHERE id <=4 ORDER by description)aaa
UNION
SELECT * FROM (SELECT * FROM table1 WHERE id >4 ORDER by description)bbb
OR
SELECT * FROM table1
ORDER BY
CASE WHEN id <=4 THEN 0
ELSE 1
END, description