Given a list of client names (comma separated) I need to find out how many exists in Client table and how many dont.
The table and input lists are both huge; here I'm just giving table as an example of my requirement.
Client
client_name
status
abc
1
def
1
ghi
0
jkl
1
Query I thought of using is
select client_name, count(client_name) over (partition by client_name) count_client from client where status = 1 and client_name in ('abc', 'xyz', 'ghi', 'jkl');
This returns:
client_name
count_client
abc
1
jkl
1
But what I need is
client_name
count_client
abc
1
xyz
0
ghi
0
jkl
1
Could someone please help me with the SQL query
If you have "huge" amount of data, then your best option is to store list of values you're interested in into a separate table. That's trivial.
Otherwise, as an alternative you could try something like this (sample data in lines #1 - 11; query begins at line #12):
SQL> WITH
2 client (client_name, status)
3 AS
4 -- this is contents of your table
5 (SELECT 'abc', 1 FROM DUAL
6 UNION ALL
7 SELECT 'def', 1 FROM DUAL
8 UNION ALL
9 SELECT 'ghi', 0 FROM DUAL
10 UNION ALL
11 SELECT 'jkl', 1 FROM DUAL)
12 -- join your table with a collection
13 SELECT t.COLUMN_VALUE AS client_name, NVL (SUM (c.status), 0) AS count_client
14 FROM client c
15 RIGHT JOIN TABLE (sys.odcivarchar2list ('abc',
16 'xyz',
17 'ghi',
18 'jkl')) t
19 ON t.COLUMN_VALUE = c.client_name
20 AND c.status = 1
21 GROUP BY t.COLUMN_VALUE
22 ORDER BY t.COLUMN_VALUE;
CLIENT_NAME COUNT_CLIENT
--------------- ------------
abc 1
ghi 0
jkl 1
xyz 0
SQL>
You should ideally maintain a separate table containing the client names of interest. Absent that, we can use a CTE to store the values, then left join to your current table:
WITH clients AS (
SELECT 'abc' AS client_name FROM dual UNION ALL
SELECT 'xyz' FROM dual UNION ALL
SELECT 'ghi' FROM dual UNION ALL
SELECT 'jkl' FROM dual
)
SELECT cl.client_name, COUNT(c.client_name) AS count_client
FROM clients cl
LEFT JOIN client c
ON c.client_name = cl.client_name AND
c.status = 1
GROUP BY cl.client_name;
Demo
Related
I have table like this:
id
name
contact
1
A
65489
1
A
1
A
45564
2
B
3
C
12345
3
C
1234
4
D
32
4
D
324
I only want users who have no contact or the contact length is not five.
If the user has two or more contacts and the length of one of them is five and the rest is not, then such users should not be included in the table.
so,If the customer has at least one contact length of five, I do not want that.
so, i want table like this:
id
name
contact
2
B
4
D
32
4
D
324
Can you halp me?
You could actually do a range check here:
SELECT id, name, contact
FROM yourTable t1
WHERE NOT EXISTS (
SELECT 1
FROM yourTable t2
WHERE t2.id = t1.id AND TO_NUMBER(t2.contact) BETWEEN 10000 AND 99999
);
Note that if contact already be a numeric column, then just remove the calls to TO_NUMBER above and compare directly.
Yet another option:
SQL> with test (id, name, contact) as
2 (select 1, 'a', 65879 from dual union all
3 select 1, 'a', null from dual union all
4 select 1, 'a', 45564 from dual union all
5 select 2, 'b', null from dual union all
6 select 3, 'c', 12345 from dual union all
7 select 3, 'c', 1234 from dual union all
8 select 4, 'd', 32 from dual union all
9 select 4, 'd', 324 from dual
10 )
11 select *
12 from test a
13 where exists (select null
14 from test b
15 where b.id = a.id
16 group by b.id
17 having nvl(max(length(b.contact)), 0) < 5
18 );
ID N CONTACT
---------- - ----------
2 b
4 d 32
4 d 324
SQL>
COUNT analytic function can also be used to get the job done.
select id, name, contact
from (
select id, name, contact
, count( decode( length(contact), 5, 1, null ) ) over( partition by id, name ) cnt
from YourTable
)
where cnt = 0
demo
This is somewhat a complex problem to describe, but I'll try to explain it with an example. I thought I would have been able to use the Oracle Instr function to accomplish this, but it does not accept queries as parameters.
Here is a simplification of my data:
Table1
Person Qualities
Joe 5,6,7,8,9
Mary 7,8,10,15,20
Bob 7,8,9,10,11,12
Table2
Id Desc
5 Nice
6 Tall
7 Short
Table3
Id Desc
8 Angry
9 Sad
10 Fun
Table4
Id Desc
11 Boring
12 Happy
15 Cool
20 Mad
Here is somewhat of a query to give an idea of what I'm trying to accomplish:
select * from table1
where instr (Qualities, select Id from table2, 1,1) <> 0
and instr (Qualities, select Id from table3, 1,1) <> 0
and instr (Qualities, select Id from table3, 1,1) <> 0
I'm trying to figure out which people have at least 1 quality from each of the 3 groups of qualities (tables 2,3, and 4)
So Joe would not be returned in the results because he does not have the quality from each of the 3 groups, but Mary and Joe would since they have at least 1 quality from each group.
We are running Oracle 12, thanks!
Here's one option:
SQL> with
2 table1 (person, qualities) as
3 (select 'Joe', '5,6,7,8,9' from dual union all
4 select 'Mary', '7,8,10,15,20' from dual union all
5 select 'Bob', '7,8,9,10,11,12' from dual
6 ),
7 table2 (id, descr) as
8 (select 5, 'Nice' from dual union all
9 select 6, 'Tall' from dual union all
10 select 7, 'Short' from dual
11 ),
12 table3 (id, descr) as
13 (select 8, 'Angry' from dual union all
14 select 9, 'Sad' from dual union all
15 select 10, 'Fun' from dual
16 ),
17 table4 (id, descr) as
18 (select 11, 'Boring' from dual union all
19 select 12, 'Happy' from dual union all
20 select 15, 'Cool' from dual union all
21 select 20, 'Mad' from dual
22 ),
23 t1new (person, id) as
24 (select person, regexp_substr(qualities, '[^,]+', 1, column_value) id
25 from table1 cross join table(cast(multiset(select level from dual
26 connect by level <= regexp_count(qualities, ',') + 1
27 ) as sys.odcinumberlist))
28 )
29 select a.person,
30 count(b.id) bid,
31 count(c.id) cid,
32 count(d.id) did
33 from t1new a left join table2 b on a.id = b.id
34 left join table3 c on a.id = c.id
35 left join table4 d on a.id = d.id
36 group by a.person
37 having ( count(b.id) > 0
38 and count(c.id) > 0
39 and count(d.id) > 0
40 );
PERS BID CID DID
---- ---------- ---------- ----------
Bob 1 3 2
Mary 1 2 2
SQL>
What does it do?
lines #1 - 22 represent your sample data
T1NEW CTE (lines #23 - 28) splits comma-separated qualities into rows, per every person
final select (lines #29 - 40) are outer joining t1new with each of "description" tables (table2/3/4) and counting how many qualities are contained in there for each of person's qualities (represented by rows from t1new)
having clause is here to return only desired persons; each of those counts have to be a positive number
Maybe this will help:
{1} Create a view that categorises all qualities and allows you to SELECT quality IDs and categories . {2} JOIN the view to TABLE1 and use a join condition that "splits" the CSV value stored in TABLE1.
{1} View
create or replace view allqualities
as
select 1 as category, id as qid, descr from table2
union
select 2, id, descr from table3
union
select 3, id, descr from table4
;
select * from allqualities order by category, qid ;
CATEGORY QID DESCR
---------- ---------- ------
1 5 Nice
1 6 Tall
1 7 Short
2 8 Angry
2 9 Sad
2 10 Fun
3 11 Boring
3 12 Happy
3 15 Cool
3 20 Mad
{2} Query
-- JOIN CONDITION:
-- {1} add a comma at the start and at the end of T1.qualities
-- {2} remove all blanks (spaces) from T1.qualities
-- {3} use LIKE and the qid (of allqualities), wrapped in commas
--
-- inline view: use UNIQUE, otherwise we may get counts > 3
--
select person
from (
select unique person, category
from table1 T1
join allqualities A
on ',' || replace( T1.qualities, ' ', '' ) || ',' like '%,' || A.qid || ',%'
)
group by person
having count(*) = ( select count( distinct category ) from allqualities )
;
-- result
PERSON
Bob
Mary
Tested w/ Oracle 18c and 11g. DBfiddle here.
i am currently working in a database that has a table that looks like this:
ID | Type | Value
11111 | Type1 | 45
11111 | Type2 | 85
11111 | Type3 | 26
11111 | Type4 | 69
11112 | Type1 | 14
11112 | Type2 | 36
11113 | Type1 | 69
11113 | Type3 | 25
This table works as followed:
Each ID appears multiple times in the table.
Each ID has one or more types.
Each type has a value
In the example above you can see that the ID 11112 does not have a Type3 or a Type4 and that ID 11113 does not have a Type2 or Type4.
The problem with this is that some of these types (with the corresponding value) should be there for every ID, to make this happen a list should be created with all the ID's that are missing one or more of these types. This is so that it's easy to see what id's need their type and value added.
Is there a query that can make such a list? One that gives the Unique ID's of every instance that is missing one or more types? I was able to get the ID with only a specific type missing, but i have not been able to make it so that the ID is listed of any type is missing.
You can generate all required types and id combinations and then filter out the ones that exist:
with required_types as (
select 'type3' as type from dual union all
select 'type4' as type from dual
)
select i.id, rt.type
from (select distinct id from t) i cross join
required_types rt left join
t
on t.id = i.id and t.type = rt.type
where t.id is null;
If all types present in the data are required, you can use (select distinct type from t).
This query shows all ID's where at least one type is missing:
SELECT ID
FROM T
GROUP BY ID
HAVING COUNT(DISTINCT TYPE)<(SELECT COUNT(DISTINCT TYPE) FROM T)
Not very elegant, but will return IDs and types they are missing. I removed superfluous "1111" from IDs and "ype" from types (was too lazy to type them all).
SQL> with test (id, type) as
2 (select 1, 't1' from dual union all
3 select 1, 't2' from dual union all
4 select 1, 't3' from dual union all
5 select 1, 't4' from dual union all
6 --
7 select 2, 't1' from dual union all
8 select 2, 't2' from dual union all
9 --
10 select 3, 't1' from dual union all
11 select 3, 't3' from dual
12 ),
13 all_types as
14 (select distinct type from test)
15 --
16 select t.id, a.type
17 from test t join all_types a on 1 = 1
18 minus
19 select t.id, t.type
20 from test t;
ID TY
---------- --
2 t3
2 t4
3 t2
3 t4
SQL>
I have a table that contains data something like this
TABLE_A
ID PARENT_ID NAME PROJECT_ID
1 abc
2 1 def
3 2 ghi
4 3 jkl 101
5 1 mno
and I have another table that contains some data that depends on first table 'project_id' :
TABLE_B
ID PROJECT_ID NAME
1 101 prs
2 101 tuv
3 102 xyz
4 102 hgf
I want a result something like this ;
abc
def
ghi
jkl
prs
tuv
mno
I have tried something like this but I did not know how to connect 'TABLE_B'
SELECT LEVEL, A.NAME
FROM TABLE_A A
CONNECT BY PRIOR A.ID = PRIOR A.PARENT_ID
ORDER BY LEVEL;
If I understand well your need, this could be a way:
/* building a test case */
with TABLE_A(ID, PARENT_ID, NAME, PROJECT_ID) as (
select 1, null, 'abc', null from dual union all
select 2, 1 , 'def', null from dual union all
select 3, 2 , 'ghi', null from dual union all
select 4, 3 , 'jkl', 101 from dual union all
select 5, 1 , 'mno', null from dual
),TABLE_B(ID, PROJECT_ID, NAME) as (
select 1, 101, 'prs' from dual union all
select 2, 101, 'tuv' from dual union all
select 3, 102, 'xyz' from dual union all
select 4, 102, 'hgf' from dual
)
/* the query */
select name
from (
select ID, PARENT_ID, NAME, PROJECT_ID
from table_a
UNION ALL
select a.ID, a.PARENT_ID, b.NAME, a.PROJECT_ID
from table_b b
inner join table_a a
on a.project_id = b.project_id
)
start with parent_id is null
connect by prior id = parent_id
The idea here is to build a partial result that contains all the data from table_a and table_b and then use this result in a hierarchical query as if it was a single table.
The result:
abc
def
ghi
jkl
prs
tuv
mno
Is there a way to extract ONLY the first row that come directly after the rows containing type = 'Email'?
Sample table:
id type details
1 Email admin#sqlfiddle.com
2 1234 1234
3 Email orange#hotmail.com
4 12345 12345
5 123456 123456
6 Email cake#hotmail.com
7 1234567 1234567
8 12345678 12345678
9 123456789 123456789
10 Email lala#gmail.com
11 01 01
12 Email apple#yahoo.ca
13 012 012
14 Email red#gmail.com
15 0123 0123
With Python and Pandas, I would write something like this...
indexes = table[table['type']=='Email'].index + 1
table = table.ix[indexes]
Where the output would be...
2 1234 1234
4 12345 12345
7 1234567 1234567
11 01 01
13 012 012
15 0123 0123
select *
from (select *
,lag (type) over (order by id) as prev_type
from t
) t
where prev_type = 'Email'
If you are specifically wanting just the first record after the email row you could be a bit more selective like this:
SELECT * FROM Table WHERE ID IN (SELECT ID+1 FROM Table where type='Email')
For SQL Server Query like this
select * from table where id in (Select id+1 from Table where type='Email')
Here's a solution that should work even if id has gaps. It uses window functions.
---Sample data
WITH data([id], [type] , [details]) AS
(
SELECT 1,'Email','admin#sqlfiddle.com' UNION ALL
SELECT 2,'1234', '1234' UNION ALL
SELECT 3,'Email','orange#hotmail.com' UNION ALL
SELECT 4,'12345','12345' UNION ALL
SELECT 5,'123456', '123456' UNION ALL
SELECT 6,'Email','cake#hotmail.com' UNION ALL
SELECT 7,'1234567', '1234567' UNION ALL
SELECT 8,'12345678', '12345678' UNION ALL
SELECT 9,'123456789','123456789' UNION ALL
SELECT 10, 'Email','lala#gmail.com' UNION ALL
SELECT 11, '01','01' UNION ALL
SELECT 12, 'Email','apple#yahoo.ca' UNION ALL
SELECT 13 , '012', '012' UNION ALL
SELECT 14 ,'Email','red#gmail.com' UNION ALL
SELECT 15 ,'0123', '0123'
),
---temporary table to hold row numbers
tbl([Row_Num], [id], [type]) AS
(
SELECT (ROW_NUMBER() OVER (ORDER BY [id])) AS [Row_Num] ,[id],[type] FROM data
)
---actual query using both tables
SELECT
d.[id],
d.[type],
d.[details]
FROM [data] d
INNER JOIN
[tbl] t
ON d.[id] = t.[id]
WHERE t.[Row_Num] IN (SELECT Row_Num + 1 FROM tbl WHERE [type] = 'Email')