Converting PL/SQL select rows in custom columns - plsql

I am stuck with this problem, I'm doing select like this:
select * from mytable;
and getting following results
-------------
|NAME |VALUE|
-------------
|nam1 |val1 |
-------------
|nam2 |val2 |
-------------
|nam3 |val3 |
-------------
Result is always formatted like this, NAME->VALUE.
Also, there are constraints placed on table so only one distinct NAME could appear in result. Also, values could be numbers, varchars, nulls, I don't want to do aggregation on this values.
Now I would like to convert this result to this:
--------------------
|NAM1 |NAM2 | NAM3 |
--------------------
|val1 |val2 | val3 |
--------------------
I tried achieving this result with pivot() function but without much success.
Thank you for your time, best regards :)
EDIT
This is the working example, with hardcoded column values, which is what I want to avoid.
select * from (select name, value from mytable)
pivot (min(value) for name in (
'nam1' as nam1
'nam2' as nam2
'nam3' as nam3
));

Using DECODE Function:
select name,
decode(name,'Name1','value1',0) as Name1,
decode(name,'Name2','value2',0) as Name2,
decode(name,'Name3','value3',0) as Name3,
decode(name,'Name4','value4',0) as Name4
from mytable
group by name
order by name;

Related

Why LEFT JOIN on LIKE statement gives different result on "_" changed to ","?

table1:
code
category
ABC
ZMLND_XY_ABC
table2:
category
label
MLND
3
ZMLND
4
I'd like to map table1's category to table2's label if the part of table1's category separated by _ equals table2's category. For example, ZMLND_XY_ABC in table1 matches ZMLND in table2 while ZMLND_XY_ABC and MLND does not.
Query:
select * from table1 left join table2 on '_'||table1.category||'_' like '%_'||table2.category||'_%'
Result:
code | category | category_1 | label
ABC | ZMLND_XY_ABC | MLND | 3 <- _MLND_ should not match
_ZMLND_XY_ABC_
ABC | ZMLND_XY_ABC | ZMLND | 4
However, I replace _ by ,:
table1:
code
category
ABC
ZMLND,XY,ABC
select * from table1 left join table2 on ','||table1.category||',' like '%,'||table2.category||',%'
Result:
code
category
category_1
label
ABC
ZMLND,XY,ABC
ZMLND
4
Why are there different results?
Because of SQLITE like the _ is as special a character as the %.
As you seem to know, the % matches multiple characteres.
What you seem to have missed is that the _ matches any single character.
Compare e.g. https://www.sqlitetutorial.net/sqlite-like/
Have a look at https://www.sqlite.org/lang_expr.html concerning the ESCAPE optional syntax part, which probably can help you to write your query.

Recursively add to a data table in SAS

I am new to SAS. I need to do x-iterations to populate my dataset called MYRS.
Each iteration needs to JOIN TABLE1 with (TABLE2+ MYRS) MINUS the records which are already in MYRS table.
Then, I need to update MYRS table with additional matches. The GOAL is to track a chain of emails.
MYRS is essentially a copy of TABLE1 and contains matching records. Kind of tricky. (simplified schema). Table1 Can have DUPS.
For example
TABLE1:
ID | EMAIL1 | EMAIL2 | EMAIL3 | EMAIL4|
1 | A | s | d | F
2 | g | F | j | L
3 | z | x | L | v
4 | z | x | L | v
2 | g | F | j | L
TABLE2:
EMAIL
A
MYRS (starts as empty dataset)
EMAIL1 | EMAIL2 | EMAIL3 | EMAIL4
Logic: TABLE1 has email that matches email in TABLE2. Therefore this record need to show up. Other records don't match anything in TABLE2. But because Record1 and Record2 share the same ALTERNATIVE email F, Record2 also need to be shown. But because Record2 and Record3 share same alternative email L, Record3 also needs to be shown. And so fourth...
proc sql;
SELECT TABLE1.id,
TABLE1.email1,
TABLE1.email2,
TABLE1.email3,
TABLE1.email4
FROM TABLE1
INNER JOIN (
SELECT EMAIL
FROM TABLE2
UNION
SELECT EMAIL1 AS EMAIL
FROM MYRS
UNION
SELECT EMAIL2 AS EMAIL
FROM MYRS
UNION
SELECT EMAIL3 AS EMAIL
FROM MYRS
UNION
SELECT EMAIL4 AS EMAIL
FROM MYRS
)
ON EMAIL=EMAIL1 OR EMAIL=EMAIL2 OR EMAIL=EMAIL3 OR EMAIL=EMAIL4
WHERE TABLE1.id NOT IN (
SELECT DISTINCT ID
FROM MYRS
)
quit;
How can I create the following logic:
Wrap this into some sort of function
Before sql execution, count amount of records in MYDS and SAVE the count
Execute SQL and update MYDS
Count amount of records in MYDS
If MYDS count did not change, stop execution
Else, goto #3
I am very new to SAS (3 days to be exact) and trying to put everything together. (I would use the logic above if I was to do that in Java)
Here is a macro approach, it mostly follows your logic but transforms your data first and the input/output is a list of IDs (you can easily get to and from emails with this).
This code will probably introduce quite a few SAS features that you are unfamiliar with, but the comments and explanations below should help . If any of it is still unclear take a look at the links or add a comment.
It expects input data:
inData: Your TABLE1 with ID and EMAIL* variables
matched: An initial list of known wanted IDs
It returns:
matched: An updated list of wanted IDs
/* Wrap the processing in a macro so that we can use a %do loop */
%macro looper(maxIter = 5);
/* Put all the emails in one column to make comparison simpler */
proc transpose data = inData out = trans (rename = (col1 = email));
by ID;
var email:;
run;
/* Initialise the counts for the %where condition */
%let _nMatched = 0;
%let nMatched = 1;
%let i = 0;
/* Loop until no new IDs are added (or maximum number of iterations) */
%do %while(&_nMatched. < &nMatched. and &i < &maxIter.);
%let _nMatched = &nMatched.;
%let i = %eval(&i. + 1);
%put NOTE: Loop &i.: &nMatched. matched.;
/* Move matches to a temporary table */
proc datasets library = work nolist nowarn;
delete _matched;
change matched = _matched;
quit;
/* Get new matched IDs */
proc sql noprint;
create table matched as
select distinct c.ID
from _matched as a
left join trans as b
on a.ID = b.ID
left join trans as c
on b.email = c.email;
/* Get new count */
select count(*) into :nMatched from matched;
quit;
%end;
%mend looper;
%looper(maxIter = 10);
The interesting bits are:
proc transpose: Converts the input into a deep table so that all the email addresses are in one variable, this makes writing the email comparison logic simpler (less repetition needed) and puts the data in a format that will make it easier for you to clean the email addresses if necessary (think upcase(), strip(), etc.).
%macro %mend: The statements used to define a macro. This is necessary as you cannot use macro logic or loops in open code. I've also added an argument so you can see how that works.
%let and select into :: Two ways to create macro variables. Macro variables are referenced with the prefix & and are used to insert text into the SAS program before it is executed.
%do %while() %end: One of the ways to perform a loop within a macro. The code within will be run repeatedly until the condition evaluates to false.
proc datasets: A procedure for performing admin tasks on datasets and libraries. Used here to delete and rename temporary tables.

Update SQL column with SELECT statement data

I am currently working with DB Browser and I have a problem with updating columns.
I have a table which contains two columns: one is filled with strings and the other one is completely empty.
I have trimmed these strings with rtrim():
SELECT rtrim(column1, 'e') FROM table1
Now I want to place these trimmed strings into the empty column:
UPDATE table1 SET column2 = (SELECT rtrim(column1, 'e') FROM table1)
But now it fills the empty column with only the first trimmed string of column1, because it won't see the SELECT statement as a column, but as a string.
I also tried:
INSERT INTO table1 (column2) SELECT rtrim(column1, 'e') FROM table1
But this just doubles the amount of rows, as column2 is not 'empty', but filled with null.
I want to get something like
| column1 | column2 |
---------------------
| apple | appl |
| orange | orang |
| grape | grap |
Any ideas on how to solve this?

In clause in oracle

I have select query like this
SELECT * FROM ACTITIVY WHERE CODE in ('L','D')
Instead of hardcording the values in IN PARAMETER I have created a PARAM table. The param table has the below values
CODE | CODE_VAL | ACTIVE
TRIGGER_XM | 'L','D' | Y
so when i rewrite the query as below
select * from ops_ACTITIVY WHERE CODE IN (
SELECT CODE_VAL FROM OPS_CONFIG_PARAMETER
WHERE CODE='TRIGGER_XML' AND ACTIVE='Y')
It doesn't work any idea how to resolve this??
You may try like this:
select * from ops_ACTITIVY t1
WHERE t1.CODE IN ( SELECT CODE_VAL FROM OPS_CONFIG_PARAMETER t2
WHERE t2.CODE='TRIGGER_XML' AND t2.ACTIVE='Y' )
As you are checking for the existence of a string inside another string you could try something like this:
select * from activity a where (
select instr(b.code_val, a.code)
from param b
where b.code = 'TRIGGER_XM'
AND active = 'Y'
) > 0;
But it will most likely only work as long as as you have singlechar codes.
Worked for me at: SQL Fiddle
which table are you really using? ACTITIVY or ops_ACTITIVY?
Assuming that those tables are the same, i would suggest that your PARAMETERS Table have the below values:
| CODE | CODE_VAL | ACTIVE |
| ----------- ----------- -------------
| TRIGGER_XM | L | Y |
| TRIGGER_XM | D | Y |
It is much more manageable, and i think makes much more sense to separate the Code Values of L and D in case you only need to select one of them.
Now, the query below would surely fetch both Codes with 'L' and 'D' values from the ops_ACTITIVY table:
select *
from ops_ACTITIVY
WHERE CODE IN (SELECT CODE_VAL
FROM OPS_CONFIG_PARAMETER
WHERE CODE ='TRIGGER_XML'
AND ACTIVE = 'Y');

Passing result variable to nested SELECT statement in Sqlite

I have the following query which works:
SELECT
SoftwareList,
Count (SoftwareList) as Count
FROM [assigned]
GROUP BY SoftwareList
This returns the following result set:
*SoftwareList* | *Count*
--------------------------
Office XP | 3
Adobe Reader | 3
Dreamewaver | 2
I can also run the following query:
SELECT
GROUP_CONCAT(LastSeen) as LastSeen
FROM [assigned]
WHERE SoftwareList = 'Dreamweaver';
Which would return the following result set:
*LastSeen*
----------
2007-9-23,2012-3-12
I wish to combine both of these queries into one, so that the following results are returned:
*SoftwareList* | *Count* | *LastSeen*
--------------------------------------------------------
Office XP | 3 | 2001-2-12,2008-3-19,2002-2-17
Adobe Reader | 3 | 2008-2-12,2009-3-20,2007-3-16
Dreamewaver | 2 | 2007-9-23,2012-3-12
I am trying this but don't know how to refer to the initial SoftwareList variable within the nested statement:
SELECT
SoftwareList,
Count (SoftwareList) as Count,
(SELECT
GROUP_CONCAT(LastSeen) FROM [assigned]
WHERE SoftwareList = SoftwareList
) as LastSeen
FROM [assigned]
GROUP BY SoftwareList;
How can I pass SoftwareList which is returned for each row, into the nested statement?
I think this is what you want:
SELECT SoftwareList, COUNT(SoftwareList) AS Count, GROUP_CONCAT(LastSeen)
FROM assigned GROUP BY SoftwareList

Resources