In Clickhouse, how can I obtain the rows, in which all columns are non-null / not empty? - clickhouse

Consider the following table:
A | B | C
+++++++++++++++++++
15 | | 25
10 | 20 | 30
which, for the sake of simplicity, contains only two rows. Now, when I issue this query
SELECT A, B, C FROM whatever
I would like to obtain only the second row, i. e. only these rows, where every value is non-null / not empty. Instead, I get both rows.
Is there a better way than doing the following? :
SELECT A, B, C FROM whatever
WHERE A != '' AND B != '' AND C !=''

You can use function empty to mark empty or non-empty rows as 1 if the row is empty and 0 if there is any data in it.
So your code will look like this
SELECT A
, B
, C
, empty(A) as empty_A
, empty(B) as empty_B
, empty(A) as empty_C
FROM whatever
WHERE empty_A = 0 AND empty_B = 0 AND C empty_C = 0
Thus, if there is at least one empty in columns A / B / C, then these rows will not fall into selection

In addition to the previous answer, just to be more straight, you can use the following code that combine the empty function with the trim one to spot also columns containing one or more white spaces.
SELECT col_1, col_2, col_3
FROM my_table
WHERE empty(trim(col_2)) = 0

Related

Oracle. Leave blank line on composite key level break

I want the output of my SQL to leave a blank line when any of three columns (a b or c below) change.
So if the table had columns a b c and d, you end up with a report like this:
a b c d #<- column names
-------
1 a a a #<- V - data itself
1 a a b
#<- level break here because 1 a b is different from 1 a a
1 a b c
From Googling, I have seen BREAK might solve this.
But from what I can make out this pertains to one column.
I then thought what if I have a computed column.
a is numeric, b & c are alphanumeric data types.. so I guess I could possibly use CONCAT plus TO_CHAR..
I am wondering if anyone can give me some pointers?
Cheers.
You can break on more than one column:
SQL> break on a skip 1 duplicates on b skip 1 duplicates on c skip 1 duplicates
SQL> select * from your_table
A B C D
---------- - - -
1 a a a
1 a a b
1 a b c
3 rows selected.
If you issue a plain break it shows what is set:
SQL > break
break on a skip 1 dup
on b skip 1 dup
on c skip 1 dup
Another option is to generate an extra column expression of your composite key, break on that, and set it not to display:
SQL> break on composite skip 1
SQ>> column composite noprint
SQL> select t.*, a||':'||b||':'||c as composite from your_table t;
A B C D
---------- - - -
1 a a a
1 a a b
1 a b c
3 rows selected.
which has the advantage of not showing multiple blank lines if more than one column changes at the same time.
I've separated the values with a colon; the idea of that is to use a character that doesn't appear in the values themselves, to avoid an accidental clash. If any of the columns could actually have a colon then pick something else more obscure.

Oracle - split table in indexed blocks

I have to deal with a little problem in Oracle...I have a table with 2 columns, the first column contains dates, the second one contains imports. The "import column" could have both NULL or not NULL values.
What I want to do is to order by the date column (and this is easy :) ) and then split the table in blocks of contiguous NULL or not NULL values in the "import column" adding a third column which numbers the blocks.
Example:
Date Import
01/01/2017 99.12
01/02/2017 18.19
01/03/2017 22.92
01/04/2017 28.10
01/05/2017
01/06/2017
01/07/2017
01/08/2017 33.78
01/09/2017 20.30
01/10/2017 12.33
01/11/2017
01/12/2017 1.68
this table should became
Date Import Block
01/01/2017 99.12 1
01/02/2017 18.19 1
01/03/2017 22.92 1
01/04/2017 28.10 1
01/05/2017 2
01/06/2017 2
01/07/2017 2
01/08/2017 33.78 3
01/09/2017 20.30 3
01/10/2017 12.33 3
01/11/2017 4
01/12/2017 1.68 5
You can use analytic functions like this:
select d, import, sum(state_change) over (order by d) as block
from
(
select d, import, import_state,
case when import_state = lag(import_state) over (order by d, import)
then 0 else 1 end state_change
from
(
select d, import, case when import is not null then 1 else 0 end as import_state
from t
)
);
(NB I renamed your DATE column to D as DATE is a reserved word).
Breaking it down, starting with the innermost query:
select d, import, case when import is not null then 1 else 0 end as import_state
from t
This adds a column import_state that is 1 when import is not null, 0 if it is null. This creates "blocks" but they are numbered 1,0,1,0,... instead of 1,2,3,4,...
The next part compares each import_state with that on the preceding row, to check for changes. Column state_change is 1 when there has been a change, 0 otherwise - so now the first row for each "block" has a 1 and the reset have a 0.
The outer part then simply sums the state_change values cumulatively to give the required result.
There may well be a simpler solution!

join with exact match value otherwise join with default value

I have a table a
c1 c2 c3 c4 value
all all all all 5
all david all Y 6
all all cd all 7
and table b
c1 c2 c3 c4
a peter cd N
b david all Y
c all cd N
I want to have get the value from table a into table b, the desired results is like this:
c1 c2 c3 c4 Value
a david cd N 5
b david ab Y 6
c all cd N 7
That is use the default "all" value if there is no close match find.
Thanks a lot!
I see a couple of possibilities assuming you have some errors in your example result data. (C2 for value 5 and C3 for value 6 are suspect to me)
Use a CTE to union the results replace all with null and use aggregation to get max value
Use a Join on value and evaluate table a's value for each c column if it's not all use it, otherwise use B's value. (IF a and b are both all it doesn't matter which we use.) this may be problematic if both values are potentially different like if Value 6 had a Y in A, and a N in B. but no such example exits in your data so I'm trusting it doesn't happen. (or if it did, picking A's value is more appropriate if it's not all)
AS a cte: (Common Table expression)
WITH cte as (
SELECT replace(c1,'all',null)
, replace(c2,'all',null)
, replace(c3,'all',null)
, replace(c4,'all',null)
, value
FROM A
UNION ALL
SELECT replace(c1,'all',null)
, replace(c2,'all',null)
, replace(c3,'all',null)
, replace(c4,'all',null)
, value
FROM b)
/* We have to eval the max as if it's null we need to replace it with all
Might be able to avoid the replacing all provided all values of c1-c4 are
greater than all... replacing just seemed safer. at a hit to performance.*/
SELECT coalesce(max(c1),'all') as c1
,coalesce(max(c2),'all') as c2
,coalesce(max(c3),'all') as c3
,coalesce(max(c4),'all') as c4
,value
FROM cte
GROUP BY value
Using a join (simpler from a maintenance and perhaps performance standpoint)
SELECT case when A.C1 <> 'all' then A.C1 else B.c1 end as C1,
case when A.C2 <> 'all' then A.C2 else B.c2 end as C2,
case when A.C3 <> 'all' then A.C3 else B.c3 end as C3,
case when A.C4 <> 'all' then A.C4 else B.C4 end as C4,
A.value --A.val = b.val so it doesn't matter which se use.
FROM A
INNER JOIN B
on A.value = B.Value
Depending on existing indexes and data volume the first approach might be better than the second.

substr function for substract the column value basis of another column in oracle

How to use substr function in oracle to substract the column value
based on another column vale in same table.
For example:suppose table abc having some column value like a=01-CEDAPR while B=AB_52MM_01-CEDAPR
Now i want to populate the column c on the basis of value AB_52MM. can any one suggest me
what is right way to achieve this .
This should be relatively straightforward. All you want to do is replace the value of a, if found in b, with nothing. Right?
WITH abc AS (
SELECT '01-CEDAPR' AS a, 'AB_52MM_01-CEDAPR' AS b
FROM dual
)
SELECT a, b, REPLACE(b, a)
FROM abc
See SQL Fiddle Demo here.
If you need to replace the _ preceding the value of a, then you might want to use REGEXP_REPLACE() (in case the _ may or may not exist):
WITH abc AS (
SELECT '01-CEDAPR' AS a, 'AB_52MM_01-CEDAPR' AS b
FROM dual
)
SELECT a, b, REGEXP_REPLACE(b, '_?' || a || '$')
FROM abc
The $ sign ensures that the value of a is anchored to the end; the ? makes _ optional.
SQL Fiddle Demo here.
Here's a couple of solutions that may or may not help you, based on the sketchy information you have provided. If they don't, then you will need to edit your question to provide more a more detailed explanation of what you're after!
with sd as (select '01-CEDAPR' a, 'AB_52MM_01-CEDAPR' b from dual) -- assumed O1 in column b was a typo
select a,
b,
regexp_replace(b, '_'||a),
substr(b, 1, instr(b, '_', -1) -1)
from sd;

find string from delimited values from delimited values (Oracle PL-SQL)

How can write a query for Oracle database such that I can find a comma delimited list of values from a column that contains comma delimited list of values. The :parameter passed to sql statement is also a comma delimited values that user selected.
For e.g
We have a column in tables that contains
1 | 'A','B','C'
2 | 'C','A'
3 | 'A','B'
on the web application interface we have multi select box that shows
A
B
C
and allows user to to select one or more items.
I want rows 1 and 2 to show up if they select A and B, If they select A only the all three should show up b/c all rows 1 to 3 have 'A' value in it.
This example will hopefully help and it matches the values irrespective of which order they appear in the string in the DB record.
Create example table:
CREATE TABLE t
(val VARCHAR2(100));
Insert records:
INSERT INTO t VALUES
('1|''A'',''B'',''C''');
INSERT INTO t VALUES
('2|''C'',''A''');
INSERT INTO t VALUES
('3|''A'',''B''');
Check values:
SELECT * FROM t;
1|'A','B','C'
2|'C','A'
3|'A','B'
Check solution for 'A':
SELECT val
FROM t
WHERE REGEXP_LIKE(val, '(A)');
1|'A','B','C'
2|'C','A'
3|'A','B'
Check solution for A and B
SELECT val
FROM t
WHERE REGEXP_LIKE(val, '(A|B).*(A|B)');
1|'A','B','C'
3|'A','B'
If you want to make sure the 1| part of the result isn't matched by anything then you could query using:
SELECT val
FROM t
WHERE REGEXP_LIKE(val, '(.\|.*)(A)');
and
SELECT val
FROM t
WHERE REGEXP_LIKE(val, '(.\|.*)(A|B).*(A|B)');
Hope this helps...
You could use a where clause with varying number of bind values, depending on the number of selected options:
TEST#PRJ> create table t (c varchar2(100));
TEST#PRJ> insert into t values ('2 | ''C'',''A''');
TEST#PRJ> insert into t values ('3 | ''A'',''B''');
TEST#PRJ> select * from t where c like '%''A''%' and c like '%''B''%';
C
----------------------------------------------------------------------------------------------------------------
1 | 'A','B','C'
3 | 'A','B'
TEST#PRJ> select * from t where c like '%''A''%';
C
----------------------------------------------------------------------------------------------------------------
1 | 'A','B','C'
2 | 'C','A'
3 | 'A','B'
If the values are stored in order you could use a single bind value:
TEST#PRJ> select * from t where c like '%''A''%''B''%';
C
-----------------------------------------------------------------------------------------
1 | 'A','B','C'
3 | 'A','B'

Resources