Get exclusive users in each table - sql

I have 4 tables as shown below
For each table I want the count of users that are present exclusively in that table (not present in other tables). The result should look something likes this
I have one way of getting desired result as shown below:
First Column:
SELECT COUNT(DISTINCT A.id) table1_only
FROM table1 A
LEFT JOIN (SELECT DISTINCT id
FROM table2
UNION
SELECT DISTINCT id
FROM table3
UNION
SELECT DISTINCT id
FROM table4) B
ON A.id = B.id
WHERE B.id IS NULL
Second Column:
SELECT COUNT(DISTINCT A.id) table2_only
FROM table2 A
LEFT JOIN (SELECT DISTINCT id
FROM table1
UNION
SELECT DISTINCT id
FROM table3
UNION
SELECT DISTINCT id
FROM table4) B
ON A.id = B.id
WHERE B.id IS NULL
Third Column:
SELECT COUNT(DISTINCT A.id) table3_only
FROM table3 A
LEFT JOIN (SELECT DISTINCT id
FROM table1
UNION
SELECT DISTINCT id
FROM table2
UNION
SELECT DISTINCT id
FROM table4) B
ON A.id = B.id
WHERE B.id IS NULL
Fourth Column:
SELECT COUNT(DISTINCT A.id) table4_only
FROM table4 A
LEFT JOIN (SELECT DISTINCT id
FROM table1
UNION
SELECT DISTINCT id
FROM table2
UNION
SELECT DISTINCT id
FROM table3) B
ON A.id = B.id
WHERE B.id IS NULL
But I wanted to know if there is any efficient and scalable way to get same result. Just for 4 tables the amount of code is too much.
Any ways of optimizing this task will be really helpful.
Sample fiddle. (This fiddle is for mysql, I am looking for a generic SQL based approach than any db specific approach)
P.S.:
There is no complusion on the result needs to be in column wise. It can be row wise as well, as shown below:

I would approach this by combining the data from all tables. Then aggregate and filter:
select which, count(*) as num_in_table_only
from (select id, min(which) as which, count(*) as cnt
from ((select id, 1 as which from table1) union all
(select id, 2 as which from table2) union all
(select id, 3 as which from table3) union all
(select id, 4 as which from table4)
) t
group by id
) i
where cnt = 1
group by which
Note: In your sample data, the ids are unique in each table. This solution assumes that is true, but can easily be tweaked to handle duplicates within a table.

Related

How do I count three different distinct values and group on an ID in MS-Access?

So I know MS-Access does not allow SELECT COUNT(DISTINCT....) FROM ..., but I am trying to find a more viable alternative to the usual standard of
SELECT COUNT(*) FROM (SELECT DISTINCT Name FROM table1)
My problem is I am trying to do three separate Count functions and group them on ID. If I use the method above, it is giving me the total unique value count for the whole table instead of the total count for only the value of ID. I tried doing
(SELECT COUNT(*) FROM (SELECT DISTINCT Name FROM table1 as T2
WHERE T2.ColumnA = T1.ColumnA)) As MyVal
FROM table1 as T1
but it tells me I need to specify a value for T1.ColumnA.
The SQL query I am trying to accomplish is this:
SELECT ID
COUNT(DISTINCT ColumnA) as CA,
COUNT(DISTINCT ColumnB) as CB,
COUNT(DISTINCT ColumnC) as CC
FROM table1
GROUP BY ID
Any ideas?
You can use subqueries. Assuming you have a table where each id occurs once:
select (select count(*)
from (select columnA
from table1 t1
where t1.id = t.id
group by columnA
) as a
) as num_a,
(select count(*)
from (select columnB
from table1 t1
where t1.id = t.id
group by columnB
) as b
) as num_b,
(select count(*)
from (select columnC
from table1 t1
where t1.id = t.id
group by columnC
) as c
) as num_c
from <table with ids> as t;
I'm not sure if you'll think this is "viable".
EDIT:
This makes it even more complicated . . . it suggests that MS Access doesn't support correlation clauses more than one level deep (might you consider switching to another database?).
In any case, the brute force way:
select a.id, a.numA, b.numB, c.numC
from ((select id, count(*) as numA
from (select id, columnA
from table1 t1
group by id, columnA
) as a
) as a inner join
(select id, count(*) as numB
from (select id, columnB
from table1 t1
group by id, columnB
) as b
) as b
on a.id = b.id
) inner join
(select id, count(*) as numC
from (select id, columnC
from table1 t1
group by id, columnC
) as c
) c
on c.id = a.id;

Merge two sql queries select and count

Hello I have two sql queries and i would like to merge or combine them
Query1:
Select TableA.Name, TableB.Date
from TableA, TableB
where ID = ID_used;
Query2:
SELECT count(Date)
FROM (SELECT DISTINCT Date
FROM TableA, TableB
group by Date;
I tried:
Select TableA.Name, TableB.Date
from TableA, TableB
where ID=ID_used inner join (SELECT count(Date)
FROM (SELECT DISTINCT Date
FROM TableA, TableB
group by Date)
But it gives syntax error in union query.
The result what I need: Name, count result.
Any Idea?
Test datas:
TableA
---------------------
Name ID
---------------------
John 1001
Peter 1002
TableB
-----------------------
Date ID_used
-----------------------
2015.05.01.AM 1001
2015.05.01.AM 1001
2015.05.01.AM 1002
2015.05.01.PM 1001
2015.05.01.PM 1001
2015.05.01.PM 1002
2015.05.01.PM 1002
2015.05.02.PM 1002
Results have to be:
John 2
Peter 3
As I understand it what you need is something like this:
(Although I am not sure because you didn't add test data and expected results)
Select t1.Name,t2.Date,
(select count(date)
from (select distinct date
FROM TableA,TableB
GROUP BY Date)) as datecount
from TableA t1
join TableB t2
on t1.ID = t2.ID_used
UPDATE: I think this is what you are looking for: SQLFIDDLE
select t1.id, t1.name, count(t2.Date)
FROM TableA t1
JOIN (
SELECT Distinct id_used,Date
FROM TableB) t2
ON t1.id = t2.id_used
GROUP by t1.id
EDIT: I tried this in ms access 2010:
NOTE: I couldn't use the keyword Date as a column name so changed to mydate
select t1.id, t1.name, count(t2.mydate) as datecount
FROM TableA t1,
(SELECT Distinct id_used,mydate
FROM TableB) t2
WHERE t1.id = t2.id_used
GROUP by t1.id,t1.name
That worked but I don't recommend using joins without the join or on keywords so here is one that is better readable and also works:
select t1.id, t1.name, count(t2.mydate) as datecount
FROM TableA t1
INNER JOIN (
SELECT Distinct id_used,mydate
FROM TableB) t2
ON t1.id = t2.id_used
GROUP by t1.id,t1.name
This returns the count of distinct dates from tableB for each Name in tableA:
Select a.Name, COUNT(DISTINCT b.Date)
from TableA AS a JOIN TableB AS b
ON ID=ID_used
group by a.Name
Use tablename before the column name
Select TableA.Name,TableB.Date from TableA, TableB where ID=ID_used;

Finding duplicate value in Oracle Sql

I have a table like below.
Id amount
--------------
10. 12345
10. 12345
12. 34567
13. 34567
As per my business requirement same id with same amount is not duplicate record. different Ids wtih same amount is duplicate record. hope you understood the requirement.
In the above sample record I have to get the duplicate amount values and its count and at the same time Id should be different.
The expected query result is 34567 and count as 2.
IF you need to display id as well,
SELECT a.*
FROM
(
SELECT id, amount, count(1) OVER (PARTITION BY amount) num_dup
FROM table1
)a
WHERE a.num_dup >1
Update. If you care only about distinct id , use COUNT(DISTINCT id) instead of COUNT(1)
More examples.
With joining another table
SELECT a.*
FROM
(
SELECT a.id, a.amount,
count(distinct a.id) OVER (PARTITION BY a.amount) num_dup
FROM table1 a
INNER JOIN table2 b ON (b.id = a.id)
)a
WHERE a.num_dup >1
Without window function and without table1.id :
SELECT a.amount, count(distinct a.id)
FROM table1 a
INNER JOIN table2 b ON (b.id = a.id)
GROUP BY a.amount
HAVING count(distinct a.id) >1 ;
Without window function and with table1.id :
SELECT b.*
FROM
(
SELECT a.amount, count(distinct a.id)
FROM table1 a
INNER JOIN table2 b ON (b.id = a.id)
GROUP BY a.amount
HAVING count(distinct a.id) >1
)a
INNER JOIN table1 b ON (b.amount = a.amount)

tsql: alternative to select subquery in join

this is my table layout simplified:
table1: pID (pkey), data
table2: rowID (pkey), pID (fkey), data, date
I want to select some rows from table1 joining one row from table2 per pID for the most recent date for that pID.
I currently do this with the following query:
SELECT * FROM table1 as a
LEFT JOIN table2 AS b ON b.rowID = (SELECT TOP(1) rowID FROM table2 WHERE pID = a.pID ORDER BY date DESC)
This way of working is slow, probabaly because it has to do a subquery on each row of table 1. Is there a way to improve performance on this or do it another way?
You can try something on these lines, use the subquery to get the latest based on the date field (grouping by the pID), then join that with the first table, this way the subquery would not have not have to be executed for each row of Table1 and will result in better performance:
Select *
FROM Table1 a
INNER JOIN
(
SELECT pID, Max(Date) FROM Table2
GROUP BY pID
) b
ON a.pID = b.pID
I have provided the sample SQL for one column using the group by, in case you need additional columns, add them to the GROUP BY clause. Hope this helps.
use the below code, and note that i added the order by Date desc to get the most resent data
select *
from table1 a
inner join table2 b on a.pID=b.pID
where b.rowID in(select top(1) from table2 t where t.pID=a.pID order by Date desc)
I am using the code below in a similar scenaro (I transcripted it to your example)
SELECT b.*
FROM table1 AS a
left outer join (
SELECT a.*
FROM table2 a
inner join (
SELECT a.pID, max(date) as date
FROM table2
WHERE date <= <max_date>
group by pID
) b ON a.pID = b.pID AND a.date = b.date
) b ON a.pID = b.pID
) b on a.pID = b.pID
The only problem with this aproach is that you have to make sure the date's don't reapet for the pID's
You can do this with the row_number() function and a subquery:
SELECT t1.*
FROM table1 t1 LEFT JOIN
(select t2.*, row_number() over (partition by pId order by rowId desc) as seqnum
from table2 t2
) t2
on t1.pId = t2.pId and t2.seqnum = 1;
Use the ROW_NUMBER() function to get a column saying which id of each row in table 2 is the first (As partitioned by the pID, and ordered by the rowDate descending)
Example:
WITH cte AS
(
SELECT
rowID AS t2RowId,
ROW_NUMBER OVER (PARTITION BY pID ORDER BY rowDate DESC) AS rowNum
FROM table2 t2
) -- gets the t2RowIds + a column which says which is the latest for each pID
SELECT t1.*, t2.*
FROM table1 t1
LEFT JOIN
(
table2 t2
JOIN cte ON t2.rowID = cte.t2RowId AND cte.rowNum = 1
) ON t1.pID = t2.pID
This is guaranteed to only return 1 item from table2 per pID, even if multiple items have the same date. You should of course ensure that the date column is indexed in table 2 for quick performance (ideally an index that also covers the PrimaryID of table2)

Oracle sql query union operation?

I have two tables. TableA and TableB. both the tables has some data with two columns as below.
TableA
---------
id Name
--- ----
1 abc
2 def
TableB
---------
id Name
--- ----
1 xyz
2 pqr
Now i would pass list of ids from my application and get same ids along with their names as:
select id, name
from TableA
where id in(1,2)
union select id, name
from TableB
where id in(1,2);
above query gives results as:
1 abc
1 xyz
2 def
2 pqr
But what i need is if same id is present in both the tables then TableB's Name should be considered but not TableA's name.
Expected output:
1 xyz
2 pqr
One more is, if TableB does not contain any data then TableA's data should be fetched.
How can i do that?
Thanks!
Please try using LEFT JOIN.
SELECT TableA.ID,
NVL(TableB.Name, TableA.Name) Name FROM
TableA LEFT JOIN TableB
ON TableA.ID=TableB.ID
WHERE TableA.ID IN (1, 2)
Try this query using simple union you can club the records
SELECT id, name from tableA where id not in (SELECT id FROM tableB)
UNION ALL
SELECT id, name from tableB
Try this:
select id, name from TableA where id in(1,2) and id not in ( select id from TableB) a union select id, name from TableB where id in(1,2);
try this
SELECT id , name from (
select id, name from TableA where id in(1,2)
union select id, name from TableB where id in(1,2)) t
GROUP BY id;
Use Union All, and exists/not exists to control which results are returned from table_a based on the existence of any records in table_b
select id,name
from (
select id,name
from table_b
union all
select id,name
from table_a
where exists (select null from table_b) and
not exists (
select null
from table_b
where table_b.id = table_a.id)
union all
select id,name
from table_a
where not exists (select null from table_b))
where id in (1,2)
http://sqlfiddle.com/#!4/36f26/3
one way of doing it if rows can be in A, A+B or B is (if tablea always has data, then techdo's answer is better):
select id, name
from (select id, name, row_number() over (partition by id order by rnk) rn
from (select id, name, 1 rnk from tableb
union all
select id, name, 2 rnk from tablea))
where rn = 1;
SELECT (case when B.id = A.id then b.id else a.id end) as id,
(case when B.id = A.id then b.name else a.name end) as name
FROM tableA a left JOIN tableB b ON (a.id = b.id)
MINUS operator - returns only unique rows returned by the first query but not by the second:
WITH tab_a AS
(
SELECT 1 id, 'abc' val FROM dual
UNION
SELECT 2, 'def' FROM dual
),
tab_b AS
(
SELECT 1, 'xyz' val FROM dual
UNION
SELECT 2, 'pqr' FROM dual
)
-- This is your query --
SELECT * FROM tab_b
MINUS
SELECT * FROM tab_a
/
ID VAL
----------
1 xyz
2 pqr
Following code is for selecting data from both the tables(A +B) and then taking out data using minus and join for the rows not required. Code can be easily modified if the requirement changes from selecting names from table A instead of table B.
select * from tableA where id in (1,2)
union
select * from tableB where id in (1,4)
minus
select a,id, a.Name from tableA a join tableB b on a.id = b.id where
a.id in (1,2);