New column based on the value of another column | Oracle? - sql

In an Oracle 11g database I have table called organizations which looks like this:
| ORGANIZATION_ID | ORGANIZATION_NAME | TREE_ORGANIZATION_ID | ORGANIZATION_RANG |
|-----------------|-------------------|----------------------|-------------------|
| 1 | Facebook | \1 | 1 |
| 2 | Instagram | \1\2 | 2 |
| 3 | Whatsapp | \1\3 | 2 |
| 4 | Alphabet | \4 | 1 |
| 5 | Nest | \4\5 | 2 |
| 6 | Google | \4\6 | 2 |
| 7 | YouTube | \4\6\7 | 3 |
As you can see this table has column called TREE_ORGANIZATION_ID where I store information about relationship of organizations.
This code returns all organizations that have a specific ID in the column TREE_ORGANIZATION_ID. In my case this code return Google and YouTube entries.
SELECT
*
FROM
ORGANIZATIONS
WHERE
TREE_ORGANIZATION_ID LIKE '%\' || '6'
OR
TREE_ORGANIZATION_ID LIKE '%\' || '6' || '\%';
I want to add new column called STATUS which looks like this:
| ORGANIZATION_ID | ORGANIZATION_NAME | TREE_ORGANIZATION_ID | ORGANIZATION_RANG | STATUS |
|-----------------|-------------------|----------------------|-------------------|----------|
| 6 | Google | \4\6 | 2 | root |
| 7 | YouTube | \4\6\7 | 3 | not root |
I tried next code but it raise error ORA-00937 not a single-group group function.
How do I create a new column based on the value of another column?
SELECT
ORGANIZATION_ID,
ORGANIZATION_NAME,
TREE_ORGANIZATION_ID,
CASE
WHEN ORGANIZATION_RANG = MIN(ORGANIZATION_RANG) THEN 'root'
ELSE 'not root'
END AS STATUS
FROM
ORGANIZATIONS
WHERE
TREE_ORGANIZATION_ID LIKE '%\' || '6'
OR
TREE_ORGANIZATION_ID LIKE '%\' || '6' || '\%';

You can try below -
SELECT
ORGANIZATION_ID,
ORGANIZATION_NAME,
TREE_ORGANIZATION_ID,
CASE
WHEN TREE_ORGANIZATION_ID LIKE '%\' || '6' THEN 'root'
when TREE_ORGANIZATION_ID LIKE '%\' || '6' || '\%' then 'not root'
END AS STATUS
FROM
ORGANIZATIONS
WHERE
TREE_ORGANIZATION_ID LIKE '%\' || '6'
OR
TREE_ORGANIZATION_ID LIKE '%\' || '6' || '\%'

You want to use an analytic function, not an aggregation function:
SELECT ORGANIZATION_ID, ORGANIZATION_NAME, TREE_ORGANIZATION_ID,
(CASE WHEN ORGANIZATION_RANG = MIN(ORGANIZATION_RANG) OVER ()
THEN 'root'
ELSE 'not root'
END) AS STATUS
FROM ORGANIZATIONS O
WHERE TREE_ORGANIZATION_ID || '\' LIKE '%\' || '6' || '\%';
Note that this also simplifies the logic for matching 6 by testing the organization id with a backslash on the end. You could also use REGEXP_LIKE() for this purpose.

Related

SQL Check if the User has IN and Out

I need help getting the User which has an 'IN' and 'Out' in Column isIN. If the user has an IN and OUT do not select them in the list. I need to select the user who has only had an IN. Please I need help. Thanks in advance.
This is the table:
| Users | IsIN |
|:------------------:|:-----:|
| MHYHDC61TMJ907867 | IN |
| MHYHDC61TMJ907867 | OUT |
| MHYHDC61TMJ907922 | IN |
| MHYHDC61TMJ907922 | OUT |
| MHYHDC61TMJ907923 | IN |
| MHYHDC61TMJ907923 | OUT |
| MHYHDC61TMJ907924 | IN | - I need to get only this row
| MHYHDC61TMJ907925 | IN |
| MHYHDC61TMJ907925 | OUT |
| MHYHDC61TMJ908054 | IN | - I need to get only this row
| MHYHDC61TMJ908096 | IN | - I need to get only this row
| MHYHDC61TMJ908109 | IN | - I need to get only this row
Need to get the result like
| Users | IsIN |
|:------------------:|:-----:|
| MHYHDC61TMJ907924 | IN |
| MHYHDC61TMJ908054 | IN |
| MHYHDC61TMJ908096 | IN |
| MHYHDC61TMJ908109 | IN |
I tried using this query and sample query below but it doesn't work.
select s.[Users], s.[isIn] [dbo].[tblIO] s
where not exists (
select 1
from [dbWBS].[dbo].[tblIO] s2
where s2.[Users] = s.[Users] and s2.isIn = 'IN'
);
You can use not exists:
select s.*
from sample s
where not exists (select 1
from sample s2
where s2.user = s.user and s2.inout = 'OUT'
);
If you want only users that meet the condition (and not the full rows):
select user
from sample s
group by user
having min(inout) = max(inout) and min(inout) = 'IN';
Bearing in mind that an 'OUT' IsIn must be always preceded by an 'IN' record, you could use a query like this:
select s.Users, 'IN' as IsIn
from sample s
group by s.Users
having count(distinct s.IsIn) = 1

Show the firebase event_params key-value data into the single row using Big Query

I am trying to perform a Google BigQuery on the Firebase stored events. I have executed the following query
SELECT * FROM `myTable` LIMIT 6
which has the following result:
+-----+----------+--------+------------------+---------------------------------+
| Row | date | name | event_params.key | event_params.value.string_value |
+-----+----------+--------+------------------+---------------------------------+
| 1 | 20200922 | Event1 | errorName | BLE_Not_connected |
| | | | appDetails | 2.2.2 |
| | | | errorDetails | iOS-Error |
+-----+----------+--------+------------------+---------------------------------+
So, here row-1 has multiple entries of event_params.key and their value shows on event_params.value.string_value column. Now, I want to perform a Google Big-Query which flattens the event_params.key column value and show a result below
+-----+----------+--------+------------------+---------------------------------+
| Row | date | name | errorName | appDetails | errorDetails |
+-----+----------+--------+------------------+---------------------------------+
| 1 | 20200922 | Event1 | BLE_Not_connected| 2.2.2 | iOS-Error |
+-----+----------+--------+------------------+---------------------------------+
Could anyone help me? Thanks in advance.
Below is for BigQuery Standard SQL
EXECUTE IMMEDIATE (
SELECT """
SELECT date, name, """ ||
STRING_AGG("""MAX(IF(key = '""" || key || """', value.string_value, NULL)) AS """ || key, ', ')
|| """
FROM `project.dataset.table` t, t.event_params
GROUP BY date, name
"""
FROM (
SELECT DISTINCT key
FROM `project.dataset.table` t, t.event_params
)
);
If to apply to sample data from your question - output is
Row date name errorName appDetails errorDetails
1 20200922 Event1 BLE_Not_connected 2.2.2 iOS-Error

Is there a way in Postgres / SQL to substitute characters in strings from columns where the characters in the string are column names?

I have a table called sentences, and a table called logs.
The sentences table looks like this:
|------------|---------------------------------------|
| id | sentence |
|------------|---------------------------------------|
| 1 | [var1] says hello! |
|------------|---------------------------------------|
| 2 | [var1] says [var2]! |
|------------|---------------------------------------|
| 3 | [var1] says [var2] and [var3]! |
|------------|---------------------------------------|
| 4 | [var4] says [var2] to [var1]! |
|------------|---------------------------------------|
The logs table looks like this:
|------------|------------------|--------------|--------------|--------------|--------------|
| id | sentenceId | var1 | var2 | var3 | var4 |
|------------|------------------|--------------|--------------|--------------|--------------|
| 1 | 1 | Sam | | | |
|------------|------------------|--------------|--------------|--------------|--------------|
| 2 | 2 | Joe | what's up | | |
|------------|------------------|--------------|--------------|--------------|--------------|
| 3 | 3 | Tim | hey | how are you | |
|------------|------------------|--------------|--------------|--------------|--------------|
| 4 | 4 | Joe | hi | | Tiffany |
|------------|------------------|--------------|--------------|--------------|--------------|
The result I am trying to get is:
|------------|-----------------------------------------|
| logs.id | sentences.sentence |
|------------|-----------------------------------------|
| 1 | [Sam] says hello! |
|------------|-----------------------------------------|
| 2 | [Joe] says [what's up]! |
|------------|-----------------------------------------|
| 3 | [Tim] says [hey] and [how are you]! |
|------------|-----------------------------------------|
| 4 | [Tiffany] says [hi] to [Joe] |
|------------|-----------------------------------------|
I'm not sure how to write the SQL query to make the database do the text substitutions for me.
I could just select everything from both tables using an inner join, and then loop through in code and do the substitutions myself. I.e.:
SELECT logs.id, sentences.sentence, logs.var1, logs.var2, logs.var3, logs.var4 FROM logs INNER JOIN sentences ON logs.sentenceId = sentences.id
And then in code:
logs.forEach(log => log.sentence.replace(/\[(.*?)\]/g, ($matchedString, $columnName) => log[$columnName] ))
But if possible, I'd like the database to do that for me so that I don't have to select more data than I need.
I would write a function to do that:
create function replace_vars(p_sentence text, p_vars jsonb)
returns text
as
$$
declare
l_rec record;
l_result text;
begin
l_result := p_sentence;
for l_rec in select * from jsonb_each_text(jsonb_strip_nulls(p_vars)) as x(var,value)
loop
l_result := replace(l_result, l_rec.var, l_rec.value);
end loop;
return l_result;
end;
$$
language plpgsql;
Then you can use it like this:
select s.id, s.sentence, replace_vars(s.sentence, to_jsonb(l)) new_sentence
from sentences s
left join logs l on l.sentenceid = s.id;
Online example
Elegance is always nice, but sometimes brute force gets it done.
with logsnn (sentenceid, var1, var2, var3,var4) as
( select sentenceid
, coalesce(var1,'')
, coalesce(var2,'')
, coalesce(var3,'')
, coalesce(var4,'')
from logs
)
select s.id
,(replace(replace(replace(replace(s.sentence
, '[var1]',l.var1)
, '[var2]',l.var2)
, '[var3]',l.var3)
, '[var4]',l.var4)
) AS sentence
from sentences s
left join logsnn l
on l.sentenceid = s.id;
If you really need the brackets on the result change the replacement settings to
'[var1]','[' || l.var1 || ']')
The answer by #JNevill is close to the same, bit I believe that one will return Null if any of var1,var2,var3, or var4 are Null. The CTE here changes Null with the empty string. Postgres does not consider the empty string the same as null.

Return list of tables and count in single query

I know about the describe command \d and select count(*) from my_schema_1.my_table_1;. However I'd like to get a neat list of the entire database, I have quite a few tables. Something like below would be nice.
my_schema_1 | mytable_1 | 12323
my_schema_2 | mytable_2 | 0
I'd basically like to loop over all the tables.
Maybe something like this (no need to execute a COUNT(*)) for each table):
EDIT new version to consider tables without projections:
SELECT
t.table_schema AS schema,
t.table_name AS table,
ZEROIFNULL(
CASE WHEN p.is_segmented IS TRUE
THEN SUM(ps.row_count) * COUNT(DISTINCT ps.node_name) // COUNT(ps.node_name)
ELSE MAX(ps.row_count)
END
) AS row_count,
CASE WHEN p.is_segmented THEN 'Yes' ELSE 'No' END AS segmented,
COUNT(DISTINCT p.projection_id) AS num_proj
FROM
v_catalog.tables t
LEFT OUTER JOIN v_monitor.projection_storage ps
ON t.table_id = ps.anchor_table_id
LEFT OUTER JOIN v_catalog.projections p
ON t.table_id = p.anchor_table_id
AND p.is_super_projection IS TRUE
GROUP BY
t.table_schema, t.table_name, p.is_segmented
ORDER BY
t.table_schema, t.table_name
;
Sample output:
schema | table | row_count | segmented | num_proj
--------+------------------------+-----------+-----------+----------
mauro | city | 5 | Yes | 2
mauro | employees | 1000000 | Yes | 2
mauro | empty | 0 | No | 0
mauro | fnames | 20 | Yes | 2
...
tpch | customer | 0 | Yes | 2
tpch | lineitem | 54010935 | Yes | 2
tpch | nation | 25 | No | 1
tpch | orders | 718277000 | Yes | 2
I did add a couple of columns: segmented (Yes/No) and num_proj. You can remove them if you want.

Sql single column to multiple on '.' delemiter

I have a column with data as abc.123, def.345 and so on. It is basically a name followed by a . and then a code. I am able to divide them using
select
LEFT(Campaign, ISNULL(NULLIF(CHARINDEX('.', Campaign + ' ') -1, -1), LEN(Campaign))),
STUFF(Campaign, 1, Len(Campaign) +1- CHARINDEX('.',Reverse(Campaign)), '')
from myTable;
Input set:
| myColumn |
| abc.123 |
| def.345 |
| 444 |
However, for data like '444' it shows:
|Name| Code |
|abc | 123 |
|def | 345 |
|444 | | (SHOULD BE => | | 444 |)
Assumptions:
Data can be without a '.' In such case we can insert the data in either Name or Code depending on datatype ie Name=>Alphanumeric, Code=>Numeric.
eg: Data like
999 => | | 999 |
a24.345 => | a24 | 345 |
a72 => | a72 | |
Use CASE expressions together with the LIKE operand in order to differentiate the cases:
SELECT
CASE
WHEN Campaign LIKE '[a-z]%' THEN LEFT(Campaign, CHARINDEX('.', Campaign + '.') - 1)
ELSE null
END AS Name,
CASE
WHEN Campaign LIKE '[0-9]%' THEN Campaign
WHEN Campaign LIKE '%.[0-9]%' THEN
RIGHT(Campaign, LEN(Campaign) - CHARINDEX('.', Campaign))
ELSE null
END AS Code
FROM myTable
You can see an example here: http://sqlfiddle.com/#!3/ae7ef7/1
Use a case statement:
select (case when myColumn like '%.%'
then <your code here>
else myColumn
end) as code