How can I select only those columns whose name matches a regular expression in PostgreSQL?
For example, how do I select only the columns whose name begins with 'A' in the following table, without explicitly enumerating them in the select list?
id A1 A2 A3 A4 A5 B
1 a b c d e f
2 g h i j k l
You will need to write a dynamic sql('select '||colname||' from (yourtable)') to accomplish this and dynamic sql should have supplied column names from the following sql:
SELECT column_name
FROM information_schema.columns
WHERE table_name = '<your table>'
AND column_name LIKE '<beginning of column name>%';
Related
I have input table as below-
id
col1
col2
time
01
abc
001
12:00
01
def
002
12:10
Required output table-
id
col1
col2
time
diff_field
01
abc
001
12:00
null
01
def
002
12:10
col1,col2
I need to compare both the rows and find all the columns for which there is difference in value and keep those column names in a new column diff_field.
I need a optimized solution for this as my table has more than 100 columns(all the columns need to be compared)
You might consider below approach:
WITH sample_table AS (
SELECT '01' id, 'abc' col1, '001' col2, '12:00' time UNION ALL
SELECT '01' id, 'def' col1, '002' col2, '12:10' time UNION ALL
SELECT '01' id, 'def' col1, '002' col2, '12:20' time UNION ALL
SELECT '01' id, 'ddf' col1, '002' col2, '12:30' time
)
SELECT * EXCEPT(curr, prev),
(SELECT STRING_AGG('col' || offset)
FROM UNNEST(SPLIT(curr)) c WITH offset
JOIN UNNEST(SPLIT(prev)) p WITH offset USING (offset)
WHERE c <> p AND offset < ARRAY_LENGTH(SPLIT(curr)) - 1
) diff_field
FROM (
SELECT *, FORMAT('%t', t) AS curr, LAG(FORMAT('%t', t)) OVER w AS prev
FROM sample_table t
WINDOW w AS (PARTITION BY id ORDER BY time)
);
Query results
Below approach has no dependency on actual columns' names or any names convention rather then only id and time
create temp function extract_keys(input string) returns array<string> language js as """
return Object.keys(JSON.parse(input));
""";
create temp function extract_values(input string) returns array<string> language js as """
return Object.values(JSON.parse(input));
""";
select t.*,
( select string_agg(col)
from unnest(extract_keys(cur)) as col with offset
join unnest(extract_values(cur)) as cur_val with offset using(offset)
join unnest(extract_values(prev)) as prev_val with offset using(offset)
where cur_val != prev_val and col != 'time'
) as diff_field
from (
select t, to_json_string(t) cur, to_json_string(ifnull(lag(t) over(win), t)) prev
from your_table t
window win as (partition by id order by time)
)
if apply to sample data in your question (or rather extended version of it that I borrowed from Jaytiger answer) - the output is
I have two tables A and B as shown below. The AccountID in A has a relationship with the AccountID in B.
A
AccountID CmpName AccFlag SysStartTime
A1 Test1 1 1/1/2020
A2 Test2 0 1/2/2020
A3 Test3 1 1/2/2020
B
ContactId AccountID ConFlag SysStartTime
C1 A1 1 1/1/2020
C2 A1 1 1/1/2020
C3 A1 0 1/1/2020
C4 A2 1 1/2/2020
I want to get the count of records in A that have 3 related records in B. I did this using a calculated column with the DAX:
getcount = COUNTROWS(RELATEDTABLE(B))
And then created another calculated column to flag the one's with getcount = 3.
But the problem is that I want to check the count of records in A that have 3 related records in B at a given time. So I need to filter by the sysStartTime's in both the tables. For example, I want to get the count of records in A that have 3 related records in B as of 1/1/2010. So the result should be 1. Please advise on how I can do this using a Measure instead of a calculated column.
You should be able to do something like this:
SUMX ( A, IF ( COUNTROWS ( RELATEDTABLE ( B ) ) = 3, 1 ) )
Assuming you have a date table with a relationship to both A and B, a slicer on that date table will apply to the measure and you can set whatever dates you want.
I have 2 tables connected to each other through col A. I want to match the column C with Col A and get the value of Col B.
For Example,
Table 1
ColA ColB Colc
a a1 b
a b1 c
c c1 a
Table2
ColA ColB
a a1
b b1
c c1
Now, I have already created relationships between Table2 and Table1 for my other calculations connecting both the tables with the colA.
Now, I am trying to match ColC from Table1 with ColA of Table2 and return the value of ColB from Table2 as MatchedOutput.
Expected output
Table1
ColA ColB Colc MatchedOutput
a a1 b b1
a b1 c c1
c c1 a a1
The DAX function for this is LOOKUPVALUE.
MatchedOutput = LOOKUPVALUE(Table2[ColB],Table2[ColA],Table1[ColC])
This looks for the value in Table2[ColB] where Table2[ColA] matches Table1[ColC].
Would you kindly be able to assist me with writing SAS script for a specific type of left join as described below?
I’m looking to do a left join of Table – A to Table B [given below], where full matching of all identifying fields or partial matching [at least 1 field] with the remaining fields in Table – B being missing/ null is also treated a missing; however, any partial/ full matching with at least one field populated in Table – B whilst being null/ missing in Table – A will be treated as non-match.
Here’s an example of input tables [A and B] and output matching analysis/ results below:
TABLE - A
S/N COL_1 COL_2 COL_3 COL_4
-----------------------------------
1 A p ii
2 A
3 B r
TABLE - B
S/N COL_1 COL_2 COL_3 COL_4
-----------------------------------
1 A p ii
2 A q
3 A
4 A p 7 ii
5 B
6 B r n
OUTPUT/ MATCHING ANALYSIS
TABLE - A TABLE - B MATCH NO MATCH
----------------------------------------
1 1 Y
1 2 N
1 3 Y
1 4 N
2 1 N
2 2 N
2 3 Y
2 4 N
3 5 Y
3 6 N
I've decided not to use join as there could be more than 4 columns to join...
First,
let's find the equals:
proc sql;
create table Equals as
select a.*,'Y' as Match, '' as No_Match from table_a as a
intersect
select b.*,'Y' as Match, '' as No_Match from table_b as b ;
quit;
Now, let's fine not equals:
proc sql;
create table Not_Equals as
select a.*,'' as Match, 'N' as No_Match from table_a as a
except
select b.*,'' as Match, 'N' as No_Match from table_b as b
union
select b.*,'' as Match, 'N' as No_Match from table_b as b
except
select a.*,'' as Match, 'N' as No_Match from table_a as a ;
quit;
and finally - let's merge the 2 data sets:
data All;
set Equals Not_Equals;
run;
I have String as below.
select b.col1,a.col2,lower(a.col3) from table1 a inner join table2 b on a.col = b.col and a.col = b.col
inner join (select col1, col2, col3,col4 from tablename ) c on a.col1=b.col2
where
a.col = 'value'
Output need to be table1,table2 and tablename from above string. please let me know the regex to get the result.
Should be a simple one :-)
SQL> WITH DATA AS(
2 select q'[select b.col1,a.col2,lower(a.col3) from table1 a inner join table2 b on
3 a.col = b.col and a.col = b.col inner join (select col1, col2, col3,col4 from tablename )
4 c on a.col1=b.col2 where a.col = 'value']' str
5 FROM DUAL)
6 SELECT LISTAGG(TABLE_NAMES, ' , ') WITHIN GROUP (
7 ORDER BY val) table_names
8 FROM
9 (SELECT 1 val,
10 regexp_substr(str,'table[[:alnum:]]+',1,level) table_names
11 FROM DATA
12 CONNECT BY level <= regexp_count(str,'table')
13 )
14 /
TABLE_NAMES
--------------------------------------------------------------------------------
table1 , table2 , tablename
SQL>
Brief explanation, so that OP/even others might find it useful :
The REGEXP_SUBSTR looks for the words 'table', it could be followed
by a number or string like 1,2, name etc.
To find all such words, I used connect by level technique, but it
gives the output in different rows.
Finally, to put them in a single row as comma separated values, I
used LISTAGG.
Oh yes, and that q'[]' is the string literal technique.