Oracle group by SQL query by matching varchar field - oracle

I have a table structure (that I did not design nor can I change) that uses a varchar field to store an attribute about the entity. I would like to write a SQL query to search for two attributes in particular and combine multiple result rows into single rows. To illustrate, my tables are similar to this:
company
=============
| id | name |
-------------
| 1 | co1 |
| 2 | co2 |
| 3 | co3 |
=============
agent
====================================
| id | name | company_id | type |
------------------------------------
| 1 | Tom | 1 | 'type1' |
| 2 | Bob | 1 | 'type2' |
| 3 | Bill | 2 | 'type1' |
| 4 | Jack | 2 | 'type2' |
| 5 | John | 3 | 'type1' |
| 6 | Joe | 3 | 'type2' |
====================================
type1 and type2 are hard-coded into the software as valid values (again, I didn't write it), so a search for these values should be successful (null is permitted). So, I must base my search off of these values.
As a novice, I could write this SQL:
select c.name, a.name, a.type
from company c
inner join agent a on c.id = a.company_id
and sort through these results in my software (Java program):
===========================
| c.name | a.name | type |
---------------------------
| co1 | Tom | type1 |
| co1 | Bob | type2 |
| co2 | Bill | type1 |
| co2 | Jack | type2 |
| co3 | John | type1 |
| co3 | Joe | type2 |
===========================
But, I was hoping there would be a way to combine the rows into something more efficient:
-- my failed attempt at writing this query
select c.name, a.name as type_1_agent, a.name as type_2_agent
from company c
inner join agent a on c.id = a.company_id
group by c.id -- ?
where -- ?
results:
======================================
| name | type_1_agent | type_2_agent |
--------------------------------------
| co1 | Tom | Bob |
| co2 | Bill | Jack |
| co3 | John | Joe |
======================================
Is this possible?

Oracle version:
WITH company (id, name) AS (
SELECT 1, 'co1' FROM DUAL UNION ALL
SELECT 2, 'co2' FROM DUAL UNION ALL
SELECT 3, 'co3' FROM DUAL
),
agent (id, name, company_id, type) AS (
SELECT 1, 'Tom', 1, 'type1' FROM DUAL UNION ALL
SELECT 2, 'Bob', 1, 'type2' FROM DUAL UNION ALL
SELECT 3, 'Bill', 2, 'type1' FROM DUAL UNION ALL
SELECT 4, 'Jack', 2, 'type2' FROM DUAL UNION ALL
SELECT 5, 'John', 3, 'type1' FROM DUAL UNION ALL
SELECT 6, 'Joe', 3, 'type2' FROM DUAL
)
SELECT
company_name, type_1_agent, type_2_agent
FROM
(SELECT company.name company_name, agent.name agent_name, type FROM company JOIN agent ON company.id = agent.company_id)
PIVOT (
MAX(agent_name) agent
FOR type IN ('type1' type_1, 'type2' type_2)
)
ORDER BY
company_name

You can do this with PIVOT functionality, like so:
select company,type_1_agent,type_2_agent
from
(select a.name as agentname, a.type as agenttype,c.name as company
from agent a
inner join company c on c.id = a.company_id
) s
pivot
(max(agentname) for agenttype in ('type1' type_1_agent,'type2' type_2_agent)) p
order by company
Demo
Of course, in this case we hard coded the values for type. This can be made dynamic to accomodate an unknown number of these values.

Related

oracle - querying NULL values in unpivot query

I want to fetch records from oracle DB where column value is NULL. Also i am using unpivot in my query. Somehow NULL values are not getting selected because of unpivot keyword. Can you please help me about how to get rows for the same when using unpivot.
EDIT:
SELECT a.emp_id, a.emp_dept, b.emp_location
FROM employee a,
location b UNPIVOT (emp_id
FOR dummy_id
IN (emp_id AS 'EMP_ID', last_date AS 'LAST_DATE'))
WHERE emp_id = 123 AND b.emp_loc_id = 'india' AND b.location IS NULL;
Use UNPIVOT INCLUDE NULLS:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE test ( id, a, b, c, d ) AS
SELECT 1, 1, 2, 3, 4 FROM DUAL UNION ALL
SELECT 2, 1, NULL, 3, NULL FROM DUAL;
Query 1:
SELECT *
FROM test
UNPIVOT INCLUDE NULLS ( value FOR name IN ( a, b, c, d ) )
Results:
| ID | NAME | VALUE |
|----|------|--------|
| 1 | A | 1 |
| 1 | B | 2 |
| 1 | C | 3 |
| 1 | D | 4 |
| 2 | A | 1 |
| 2 | B | (null) |
| 2 | C | 3 |
| 2 | D | (null) |

join order step by step in oracle

I have a trouble with below query.I want to join columns in step.
For example a.code first b.name1 if not match then join name2 etc.But i cant handle the query.
SELECT * FROM TABLE A
LEFT JOIN TABLE B
ON A.CODE = B.NAME1
OR A.CODE = B.NAME2
OR UPPER(B.NAME3) = UPPER(A.NAME)
Thanks
edit
with sample below,
if TABLEB.CODE = TABLEA.NAME1 match then dont want to look OR TABLEB.CODE = TABLEA.NAME2.
And if not matching any TABLEB.CODE = TABLEA.NAME1 then step by step match the columns on that order.
WITH TABLEA AS (SELECT 13445 AS ID,'A' AS TYPE,'DFSF' AS NAME1 , 'PCK' AS NAME2 FROM DUAL
UNION ALL
SELECT 13445 AS ID,'A' AS TYPE,'PCK' AS NAME1 , 'PCK' AS NAME2 FROM DUAL),
TABLEB AS (SELECT 56544 AS ID, 'PCK' AS CODE, 'PCK' AS FRST_NM FROM DUAL)
SELECT * FROM TABLEA
LEFT JOIN TABLEB
ON TABLEB.CODE = TABLEA.NAME1
OR TABLEB.CODE = TABLEA.NAME2
OR UPPER(TABLEB.FRST_NM) = UPPER(TABLEA.NAME2)
I believe your query will do what you want. There is no step by step in join conditions if you have already mentioned OR. So if either of the condition match, the rows will be matched. So if A.CODE <> B.NAME1 but A.CODE = B.NAME2, they will match. If both are true, then also they will match. I create a CTE with sample data to see the output. It will return 3 rows for 3 matches.
WITH TBL(SEQ,CODE,NAME,NAME1,NAME2,NAME3) AS
(
SELECT 1,'A1','B','A1','D','E' FROM DUAL UNION ALL -- as A.CODE(A1)=B.NAME1, it will match
SELECT 2, 'B2','B','D','B2','F' FROM DUAL UNION ALL --as A.CODE (B2) <> B.NAME1 , it will match for B.NAME2
SELECT 3, 'G','H1','D','A','H1' FROM DUAL UNION ALL --match for UPPER(B.NAME3) = UPPER(A.NAME)
SELECT 4,'P4','Q4','R4','S4','T4' FROM DUAL
)
SELECT * FROM TBL A
LEFT JOIN TBL B
ON (A.CODE = B.NAME1
OR A.CODE = B.NAME2
OR UPPER(B.NAME3) = UPPER(A.NAME)
)
Column SEQ is just to see what rows are returning.
Output
+-----+------+------+-------+-------+-------+-------+--------+--------+---------+---------+---------+
| SEQ | CODE | NAME | NAME1 | NAME2 | NAME3 | SEQ_1 | CODE_1 | NAME_1 | NAME1_1 | NAME2_1 | NAME3_1 |
+-----+------+------+-------+-------+-------+-------+--------+--------+---------+---------+---------+
| 1 | A1 | B | A1 | D | E | 1 | A1 | B | A1 | D | E |
| 2 | B2 | B | D | B2 | F | 2 | B2 | B | D | B2 | F |
| 3 | G | H1 | D | A | H1 | 3 | G | H1 | D | A | H1 |
| 4 | P4 | Q4 | R4 | S4 | T4 | | | | | | |
+-----+------+------+-------+-------+-------+-------+--------+--------+---------+---------+---------+

update one row only in oracle sqldevelop

I am newbie in oracle sqldevelop.
i have trouble in update table like this.
table1
+----+-----------+------------+
| id | acoount | date |
+----+-----------+------------+
| | John | 2/6/2016 |
| | John | 2/6/2016 |
i have table with name table1. i want to update id where the update only one rows.
Example :
+----+-----------+------------+
| id | acoount | date |
+----+-----------+------------+
| 1 | John | 2/6/2016 |
| 2 | John | 2/6/2016 |
how the solution. Thanks
Oracle Setup:
CREATE TABLE table1 ( id, account, "date" ) AS
SELECT CAST( NULL AS INT ), 'John', DATE '2016-06-02' FROM DUAL UNION ALL
SELECT CAST( NULL AS INT ), 'John', DATE '2016-06-02' FROM DUAL;
Update:
MERGE INTO TABLE1 d
USING ( SELECT ROWNUM AS id, ROWID AS rid FROM Table1 ) s
ON ( d.ROWID = s.RID )
WHEN MATCHED THEN
UPDATE SET d.id = s.id;
Result:
SELECT * FROM table1;
ID ACCOUNT date
---------- ------- -------------------
1 John 2016-06-02 00:00:00
2 John 2016-06-02 00:00:00

add column check for format number to number oracle

I need to add a column to a table that check for input to be a max value of 999 to 999, like a soccer match score. How do I write this statement?
example:
| Score |
---------
| 1-2 |
| 10-1 |
|999-999|
| 99-99 |
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE SCORES (Score ) AS
SELECT '1-2' FROM DUAL
UNION ALL SELECT '10-1' FROM DUAL
UNION ALL SELECT '999-999' FROM DUAL
UNION ALL SELECT '99-99' FROM DUAL
UNION ALL SELECT '1000-1000' FROM DUAL;
Query 1:
SELECT SCORE,
CASE WHEN REGEXP_LIKE( SCORE, '^\d{1,3}-\d{1,3}$' )
THEN 'Valid'
ELSE 'Invalid'
END AS Validity
FROM SCORES
Results:
| SCORE | VALIDITY |
|-----------|----------|
| 1-2 | Valid |
| 10-1 | Valid |
| 999-999 | Valid |
| 99-99 | Valid |
| 1000-1000 | Invalid |

List customer ID, name and all of his/her accounts

customers:
+------------+--------------+
| cid | Name |
+------------+--------------+
| 1 | Bob |
| 2 | John |
| 3 | Jane |
+------------+--------------+
accounts:
+------------+--------------+
| aid | type |
+------------+--------------+
| 1 | Checking |
| 2 | Saving |
| 3 | CD |
+------------+--------------+
transactions:
+------------+--------------+--------------+
| tid | cid | aid |
+------------+--------------+--------------+
| 1 | 1 | 1 |
| 2 | 2 | 1 |
| 3 | 1 | 2 |
| 4 | 2 | 3 |
| 5 | 3 | 1 |
+------------+--------------+--------------+
I am trying to write a plsql procedure that, given the customer id as a parameter, will display his/her id, name and all accounts. Displaying the id and name is simple enough. What I'm not sure about is how to get all the accounts that are linked to the customer id and how to retrieve more than a single account.
An ideea can be:
select c.cid, c.name, a.type
from customers c
left join transactions t on (t.cid = c.cid)
left join accounts a on (a.aid = t.aid)
where c.cid = :customer_id
group by c.cid, c.name, a.type;
the group by is needed because can be more transactions.
Further, if you want to see one line:
select cid, name, LISTAGG(type, ',') WITHIN GROUP (ORDER BY type) as account_types
from(
select distinct c.cid, c.name, a.type
from customers c
left join transactions t on (t.cid = c.cid)
left join accounts a on (a.aid = t.aid)
where c.cid = :customer_id
)
group by cid, name;
Putting this into a stored procedure/function is too simple, so I let it to you.

Resources