Related
I am trying to solve the matching of different customers, from different entities. Using only SQL select and "with" syntax, no PL/SQL. Below is a simple example. There are three customers, who are each 3 different individuals, but are recorded slightly different in different entities (1,2,3). I am matching them by P1, P2 and P3 field. In addition, P2 and P3 needs to have a cross-field check.
Customer David - only in entity 1 and 2, in both entities has P1 = 100. Easy match. Gets 'G1'
Customer Lloyd - only in entity 1 and 3, matches with cross-field check of P2 = P3. Gets 'G2'
Customer Mark - in all three entities. In entity 1 and 3 they match by P1. But the row in entity 2 matches only with cross-field check with entity 1, P2. This person needs to have 'G3' assigned in all three entities. Thoughest case.
The G, eg. G3243 represents customer's unique group ID. It is the same for one customer inside different entities and different from all other customers' Group IDs.
Any ideas how to do this? Sample table below:
table
sample data:
with
sample as (
select 1 as entity, 'DAVID' as customer, 100 as P1, 'hjk' as P2, null as P3 from dual
union all
select 2 as entity, 'DAVID' as customer, 100 as P1, 'heeee' as P2, null as P3 from dual
union all
select 1 as entity, 'Lloyd' as customer, null as P1, 'dfe' as P2, null as P3 from dual
union all
select 1 as entity, 'LLOYD' as customer, null as P1, null as P2, 'dfe' as P3 from dual
union all
select 1 as entity, 'MARK' as customer, 300 as P1, 'abc' as P2, null as P3 from dual
union all
select 2 as entity, 'MARC' as customer, 0 as P1, null as P2, 'abc' as P3 from dual
union all
select 3 as entity, 'Mark' as customer, 300 as P1, 'texttt' as P2, null as P3 from dual
)
select * from sample;
If I got it right the expected outcome should say how many correlations are present in the data for each customer. Column P1 is considered on its own and columns P2 and P3 define possible additional ties if they are cross related. Here is one possible solution using your initial sample data.
I try to count relations on columns P1, P2 and P3 by creating cte ("sample_counts")
sample_counts AS
(
Select
s1.ENTITY "ENTITY",
UPPER(s1.CUSTOMER) "CUSTOMER",
Max(CASE WHEN s1.P1 Is Not Null THEN 1 ELSE 0 END) "CNT_P1",
Max((Select Count(*) From sample Where P2 = s1.P3 )) "CNT_P2_P3",
Max((Select Count(*) From sample Where P3 = s1.P2 )) "CNT_P3_P2",
Max(Nvl((Select UPPER(CUSTOMER) From sample Where P2 = s1.P3 ), UPPER(s1.CUSTOMER))) "CUSTOMER_2"
From
sample s1
Group By
UPPER(s1.CUSTOMER), s1.ENTITY
Order By
UPPER(s1.CUSTOMER), s1.ENTITY
)
/*
"sample_counts" (cte) resulting dataset
ENTITY CUSTOMER CNT_P1 CNT_P2_P3 CNT_P3_P2 CUSTOMER_2
---------- -------- ---------- ---------- ---------- ----------
1 DAVID 1 0 0 DAVID
2 DAVID 1 0 0 DAVID
1 LLOYD 0 1 1 LLOYD
2 MARC 1 1 0 MARK
1 MARK 1 0 1 MARK
3 MARK 1 0 0 MARK
*/
The resulting dataset gives us counts of relations (as 1 or 0) over different columns for each customer and did some naming unification in column CUSTOMER_2. Even from this dataset it is obvious that David has just one level of correlations (CNT_P1), that Lloyd has two levels by cross joinning CNT_P2 and CNT_P3 (none on CNT_P1), and finally that Mark (or Marc) has correlation on all three columns. As dataset is grouped by, in the main SQL we should join it to your sample data in order to get all rows but now with Gx representing the group correlations as G1, G2, G3.
Here is the final SQL with the result:
SELECT
sc.ENTITY "ENTITY",
sc.CUSTOMER_2 "CUSTOMER",
s.P1 "P1",
s.P2 "P2",
s.P3 "P3",
'G' || To_Char(
Max(CNT_P1) OVER (PARTITION BY sc.CUSTOMER_2 ORDER BY sc.CUSTOMER_2) +
Max(CNT_P2_P3) OVER (PARTITION BY sc.CUSTOMER_2 ORDER BY sc.CUSTOMER_2) +
Max(CNT_P3_P2) OVER (PARTITION BY sc.CUSTOMER_2 ORDER BY sc.CUSTOMER_2)
) "GRP"
FROM
sample_counts sc
INNER JOIN
sample s ON(s.ENTITY = sc.ENTITY And UPPER(s.CUSTOMER) = sc.CUSTOMER)
ORDER BY
CUSTOMER_2, ENTITY
--
-- R e s u l t :
--
-- ENTITY CUSTOMER P1 P2 P3 GRP
-- ---------- -------- ---------- ------ --- --------
-- 1 DAVID 100 hjk G1
-- 2 DAVID 100 heeee G1
-- 1 LLOYD dfe G2
-- 1 LLOYD dfe G2
-- 1 MARK 300 abc G3
-- 2 MARK 0 abc G3
-- 3 MARK 300 texttt G3
If you want to have the original customer name from your data then just select s.CUSTOMER here (instead of sc.CUSTOMER_2).
Try it with your actual data. I realy do hope this would be usefull. Regards...
A D D I T I O N
Please try this code on your data. I added a few more rows (John and Anne) in the sample data and changed the code according to the comments. Hope it could help you now:
with
sample as
(
select 1 as entity, 'DAVID' as customer, 100 as P1, 'hjk' as P2, null as P3 from dual union all
select 2 as entity, 'DAVID' as customer, 100 as P1, 'heeee' as P2, null as P3 from dual union all
select 1 as entity, 'JOHN' as customer, 200 as P1, 'hjklm' as P2, null as P3 from dual union all
select 2 as entity, 'JOHN' as customer, 200 as P1, 'xdxxdxd' as P2, null as P3 from dual union all
select 1 as entity, 'Lloyd' as customer, null as P1, 'dfe' as P2, null as P3 from dual union all
select 1 as entity, 'LLOYD' as customer, null as P1, null as P2, 'dfe' as P3 from dual union all
select 1 as entity, 'ANNE' as customer, null as P1, Null as P2, 'xls' as P3 from dual union all
select 2 as entity, 'ANNE' as customer, null as P1, 'xls' as P2, Null as P3 from dual union all
select 1 as entity, 'MARK' as customer, 300 as P1, 'abc' as P2, null as P3 from dual union all
select 2 as entity, 'MARC' as customer, 0 as P1, null as P2, 'abc' as P3 from dual union all
select 3 as entity, 'Mark' as customer, 300 as P1, 'texttt' as P2, null as P3 from dual
),
sample_counts AS
(
Select
ROWNUM "RN", s1.ENTITY "ENTITY", UPPER(s1.CUSTOMER) "CUSTOMER", CASE WHEN s1.P1 Is Not Null THEN 1 ELSE 0 END "CNT_P1",
Nvl(To_Char(s1.P1), '0') "P1", (Select Count(*) From sample Where P2 = s1.P3 ) "CNT_P2_P3", Nvl(To_Char(s1.P2), '0') "P2",
(Select Count(*) From sample Where P3 = s1.P2 ) "CNT_P3_P2", Nvl(To_Char(s1.P3), '0') "P3",
Nvl((Select UPPER(CUSTOMER) From sample Where P2 = s1.P3 ), UPPER(s1.CUSTOMER)) "CUSTOMER_2"
From sample s1
Order By RN
),
p_data AS
(
Select RN, CUSTOMER_2, 'P1' "COL", To_Char(P1) "PX", To_Char(CNT_P1) "CNT_X" From sample_counts Union All
Select RN, CUSTOMER_2, 'P2' "COL", To_Char(P2) "PX", To_Char(CNT_P2_P3) "CNT_X" From sample_counts Union All
Select RN, CUSTOMER_2, 'P3' "COL", To_Char(P3) "PX", To_Char(CNT_P3_P2) "CNT_X" From sample_counts
Order By RN, COL
),
matches AS
(
SELECT p.RN, p.CUSTOMER_2, p.COL, p.PX, p.CNT_X, sc.P1, sc.P2, sc.P3
FROM p_data p
INNER JOIN sample_counts sc ON(sc.RN = p.RN)
WHERE p.CNT_X <> '0'
ORDER BY p.RN
)
SELECT DISTINCT
sc.ENTITY, sc.CUSTOMER_2, sc.P1 "P1", sc.P2 "P2", sc.P3 "P3",
'G' || To_Char(Min(m.RN) OVER(PARTITION BY sc.CUSTOMER_2 ORDER BY sc.CUSTOMER_2)) "GRP"
FROM
sample_counts sc
INNER JOIN
matches m ON (m.RN = sc.RN)
ORDER BY
sc.CUSTOMER_2, sc.ENTITY
--
-- R e s u l t :
--
-- ENTITY CUSTOMER_2 P1 P2 P3 GRP
-- ---------- ---------- ---------------------------------------- ------- --- -----------------------------------------
-- 1 ANNE 0 0 xls G7
-- 2 ANNE 0 xls 0 G7
-- 1 DAVID 100 hjk 0 G1
-- 2 DAVID 100 heeee 0 G1
-- 1 JOHN 200 hjklm 0 G3
-- 2 JOHN 200 xdxxdxd 0 G3
-- 1 LLOYD 0 0 dfe G5
-- 1 LLOYD 0 dfe 0 G5
-- 1 MARK 300 abc 0 G9
-- 2 MARK 0 0 abc G9
-- 3 MARK 300 texttt 0 G9
I have 3 tables say company, department and employee. Now I want to find out all the employees who works in company A under department D and want to display data for inner as well as left outer join to see any department that does not have any employee and company which does not have any department.
There is parent key relation between all this tables
**TABLE COMPANY**
COMPANY_NAME COMPANY ID
C1 COMP1
C2 COMP2
C3 COMP3
**TABLE DEPARTMENT**
DEPARTMENT_NAME COMPANY_NAME
D1 C1
D2 C1
D3 C2
**TABLE EMPLOYEE**
EMPLOYEE_ID DEPARTMENT_NAME
E1 D1
E2 D1
E3 D1
E4 D2
E5 D2
Company -- > Department --- > Employee.
I also want to display entity against each column as dummy column.
ENTITY COMPANY_NAME DEPARTMENT_NAME EMPLOYEE_ID
COMPANY C1 - -
DEPARTMENT C1 D1
EMPLOYEE C1 D1 E1
EMPLOYEE C1 D1 E2
EMPLOYEE C1 D1 E3
DEPARTMENT C1 D2 -
EMPLOYEE C1 D2 E4
EMPLOYEE C1 D2 E5
COMPANY C2 - -
DEPARTMENT C2 D3 -
COMPANY C3 - -
You can use the SET operator UNION ALL as follows:
SELECT 'COMPANY' AS ENTITY,
COMPANY_NAME,
'-' AS DEPARTMENT_NAME,
'-' AS EMPLOYEE_ID
FROM COMPANY C
UNION ALL
SELECT 'DEPARTMENT' AS ENTITY,
C.COMPANY_NAME,
D.DEPARTMENT_NAME,
'-' AS EMPLOYEE_ID
FROM DEPARTMENT D
JOIN COMPANY C ON C.COMPANY_ID = D.COMPANY_ID
UNION ALL
SELECT 'EMPLOYEE' AS ENTITY,
C.COMPANY_NAME,
D.DEPARTMENT_NAME,
E.EMPLOYEE_ID
FROM DEPARTMENT D
JOIN COMPANY C ON C.COMPANY_ID = D.COMPANY_ID
JOIN EMPLOYEE E ON E.DEPARTMENT_NAME = D.DEPARTMENT_NAME
ORDER BY COMPANY_NAME,
CASE WHEN DEPARTMENT_NAME = '-' THEN 1 ELSE 2 END,
DEPARTMENT_NAME,
CASE WHEN EMPLOYEE_ID = '-' THEN 1 ELSE 2 END,
EMPLOYEE_ID
Please try this:
select
case entity_disp
when 1 then 'COMPANY'
when 2 then 'DEPARTMENT'
when 3 then 'EMPLOYEE'
end as entity,
company_name,
department_name,
employee_id
from
(select
c.company_name,
d.department_name,
e.employee_id,
1 + decode(d.department_name, null, 0, 1) + decode(e.employee_id, null, 0, 1) as entity_disp
from
company c
left outer join department d on (d.company_name = c.company_name)
left outer join employee e on (e.department_name = d.department_name)
) c
order by c.entity_disp
Take into consideration that according yor tabel model expample it looks like that Company and Department tabels are joined based on company_name. Also Department and Employee tables are joined based on Department_name.
This is how select is implemented. If needed - rework joins with proper relational columns.
T-SQL, need help with combining 2 values in an column when Pivoting.
I have a Employee table with the below data -
EmpId Status L#
E1 A 1
E1 B 1
E2 A 2
E2 B 2
E3 B 3
E3 C 3
E3 D 3
and a Supervisor Table -
EmpId Sup
E1 S1
E2 S2
E3 S3
I would like to combine the values for L# when the Status is B or C
EmpId Sup A B,C D
E1 S1 1 1 0
E2 S2 1 2 0
E3 S3 0 2 1
Just use conditional aggregation. I am a little unclear what the results are. The following counts the statuses:
select s.empid, s.sup,
sum(case when status = 'A' then 1 else 0 end) as a,
sum(case when status in ('B', 'C') then 1 else 0 end) as bc,
sum(case when status = 'D' then 1 else 0 end) as d
from employee e join
supervisor s
on e.empid = s.empid
group by s.empid, s.sup;
This may help
DECLARE #TableEmp TABLE (EmpId VARCHAR(10), Status VARCHAR(10), L# INT)
INSERT INTO #TableEmp VALUES
('E1', 'A', 1 ),
('E1', 'B', 1 ),
('E2', 'A', 2 ),
('E2', 'B', 2 ),
('E3', 'B', 3 ),
('E3', 'C', 3 ),
('E3', 'D', 3 )
DECLARE #TableSup TABLE(EmpId VARCHAR(10), Sup VARCHAR(10))
INSERT INTO #TableSup VALUES
('E1', 'S1'),
('E2', 'S2'),
('E3', 'S3')
SELECT EmpId, Sup ,ISNULL(A,0) A, ISNULL(B,0) B,ISNULL(C,0) C,ISNULL(D,0) D
FROM(
SELECT e.EmpId, Sup,Status,ISNULL(L#,0) L#
FROM #TableEmp e
LEFT JOIN #TableSup s ON e.EmpId = s.EmpId
) p
PIVOT(
SUM(L#)
FOR Status IN (A, B, C,D)
) piv
The following query should do what you want:
CREATE TABLE #emp (EmpID VARCHAR(10), [Status] VARCHAR(10), L# INT)
CREATE TABLE #sup (EmpID VARCHAR(10), [Sup] VARCHAR(10))
INSERT INTO #emp VALUES
('E1','A',0),
('E1','B',1),
('E2','A',2),
('E2','B',2),
('E3','B',3),
('E3','C',5),
('E3','D',5)
INSERT INTO #sup VALUES
('E1','S1'),
('E2','S2'),
('E3','S3')
SELECT * FROM (
SELECT e.EmpID, s.Sup, t.[Status], COUNT(L#) AS [Cnt]
FROM #emp e
INNER JOIN #sup s ON e.EmpID = s.EmpID
CROSS APPLY (VALUES(CASE WHEN e.[Status] IN ('B','C') THEN 'B,C' ELSE e.[Status] END)) AS T([Status])
GROUP BY e.EmpID, s.Sup, t.[Status] ) A
PIVOT (MAX([Cnt]) FOR [Status] IN ([A],[B,C],[D])) pvt
The result is as below,
EmpID Sup A B,C D
E1 S1 1 1 NULL
E2 S2 1 1 NULL
E3 S3 NULL 2 1
I have a table like below. I need to find out the employes who have rank R1 but never have rank C1 and C2.
Id ECode Name Rank
1 EMP1 AA R1
2 EMP2 BB R1
3 EMP1 AA R2
4 EMP1 AA C1
5 EMP1 AA C2
6 EMP1 AA C3
7 EMP2 BB C4
8 EMP2 BB C5
9 EMP3 CC R1
10 EMP3 CC C1
11 EMP3 CC C2
12 EMP3 CC C4
13 EMP4 DD R1
14 EMP4 DD C3
One approach uses aggregation by employee:
SELECT ECode, Name
FROM yourTable
GROUP BY ECode, Name
HAVING
SUM(CASE WHEN Rank = 'R1' THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN Rank IN ('C1', 'C2') THEN 1 ELSE 0 END) = 0
Try this:
SELECT *
FROM EMPLOYES A
WHERE RANK = 'R1'
AND NOT EXISTS(SELECT 1
FROM EMPLOYES B
WHERE B.ECODE = A.ECODE
AND RANK IN ('C1','C2')
AND ROWNUM = 1)
I would do:
SELECT ecode, name
FROM t
WHERE rank IN ('R1', 'C1', 'C2')
GROUP BY ecode, name
HAVING MIN(rank) = MAX(rank) AND MAX(rank) = 'R1';
Evidently use NOT EXISTS :
select *
from mytable t
where t.rank = 'R1'
and not exists ( select ECode from mytable where ECode = t.ecode and rank in ('C1','C2') );
You can use combination of exists and not exists:
select *
from table t
where exists (select 1 from table where ECode = t.ECode and Rank = 'R1') AND
not exists (select 1 from table where ECode = t.ECode and Rank IN ('C1', 'C2'))
Select * from (tablename)
where Rank = 'R1'
and Rank not in (Select Rank from (tablename)
where Rank = 'C1'
or Rank = 'C2')
I might be confusing syntax from other languages with SQL and that's why this isn't working, my subquery is definitely incorrect.
Having the following 2 tables:
TEST_RESULTS
Student_ID Test_ID Test_Result
A1 234 90
B2 234 80
C3 345 85
D4 234 95
A1 345 95
C3 456 95
TEST_DESCRIPTION
Test_ID Test_Description Passing_Score
234 Test A 85
345 Test B 90
456 Test C 95
I want to calculate the rate of passing for each test and sort by it.
The output I am looking for:
Test_ID Test_Description students_taking students_passing rate
456 Test C 1 1 1
234 Test A 3 2 0.666666667
345 Test B 2 1 0.5
This is my query
SELECT td.Test_ID, td.Test_Description, COUNT(tr.Student_ID) as
students_taking, students_passing, students_passing/students_taking as rate
FROM
(SELECT td.Test_ID, td.Test_Description, COUNT(tr.Student_ID) as
students_passing
FROM TEST_RESULTS tr
JOIN TEST_DESCRIPTION td
on tr.Test_ID = td.Test_ID
WHERE tr.Test_Result > td.)
GROUP BY td.Test_ID, td.Test_Description
ORDER BY rate DESC, td.Test_ID, td.Test_Description
My select from select is wrong, because I am getting no results for this query.
I'm using CTE, LEFT JOIN for getting the desired result.
Try this query --
;WITH CTE
AS
(
SELECT
TD.TEST_ID,
TEST_DESCRIPTION,
COUNT(TR.STUDENT_ID) AS STUDENTS_TAKING,
COUNT(CASE WHEN TR.TEST_RESULT >= TD.PASSING_SCORES THEN
TR.STUDENT_ID END) AS STUDENTS_PASSING
FROM TEST_DESCRIPTION TD
LEFT JOIN TEST_RESULTS TR
ON TD.TEST_ID = TR.TEST_ID
GROUP BY TD.TEST_ID,
TEST_DESCRIPTION
)
SELECT
TEST_ID,
TEST_DESCRIPTION,
STUDENTS_TAKING,
STUDENTS_PASSING,
STUDENTS_PASSING / CONVERT (DECIMAL(4,2),STUDENTS_TAKING) AS RATE
FROM CTE
ORDER BY TEST_DESCRIPTION
Please check below query-
SELECT TD.TEST_ID,
TD.TEST_DESCRIPTION,
STUDENT_TAKING,
STUDENT_PASSING,
RATE
FROM TEST_DESCRIPTION TD,
(SELECT TR.TEST_ID,COUNT(TR.STUDENT_ID) "STUDENT_TAKING",
COUNT(CASE WHEN TEST_RESULT>=PASSING_SCORE THEN STUDENT_ID END) STUDENT_PASSING,
TO_NUMBER(TO_CHAR(COUNT(CASE WHEN TEST_RESULT>=PASSING_SCORE THEN STUDENT_ID END)/COUNT(TR.STUDENT_ID),'9999.99')) RATE
FROM TEST_RESULTS TR,TEST_DESCRIPTION TD
WHERE TR.TEST_ID=TD.TEST_ID
GROUP BY TR.TEST_ID)SUB
WHERE SUB.TEST_ID=TD.TEST_ID ORDER BY RATE DESC;
SELECT td.Test_ID, td.Test_Description,
students_taking = counts.students_taking,
students_passing = counts.students_passing,
rate = counts.rate
FROM TEST_DESCRIPTION td
OUTER APPLY (
SELECT
students_taking = COUNT(1),
students_passing = COUNT(CASE WHEN tr.Test_Result > td.Passing_score THEN 1 ELSE NULL END),
rate = IIF(COUNT(1) <> 0, COUNT(CASE WHEN tr.Test_Result > td.Passing_score THEN 1 ELSE NULL END) / CAST(COUNT(1) AS FLOAT), 0)
FROM TEST_RESULTS tr
WHERE tr.Test_ID = td.Test_ID
) counts
ORDER BY counts.rate DESC, td.Test_ID