SQL Select all from table A with counting table B - sql

If I needed a query such that I grab all columns from table A but I also need to count how many B's each row in table A has.
Table A: id | username | email | address
Table B: user_id
SELECT *, total
FROM table_a
WHERE total = (SELECT * FROM table_b WHERE table_a.id==table_b.user_id)
Any ideas?
Edit: For more clarification here is the desired output
1 | steve | steve#steve.steve | 123 Steve | 5 // letters
2 | chris | chris#chris.chris | 123 chris | 2 // letters

SELECT
table_a.id,
table_a.username,
table_a.email,
table_a.address,
count(table_b.user_id) as total
FROM table_a
LEFT OUTER JOIN table_b
ON table_a.id = table_b.user_id
GROUP BY (
table_a.id,
table_a.username,
table_a.email,
table_a.address
)
This is a good example of needing an outer join. If we used an inner join, the query would exclude the entries in table_a which has zero table_b entries.
This could be further refined to meet two challenges:
include all of the columns of table_a without explicitly asking for them.
Handle the zero entry scenario without using non-standard SQL (eg ISNULL, WHERE)
This code below should do it.
SELECT
table_a.*,
tempTable.total
FROM (
SELECT
table_a.Id,
COUNT(table_b.user_id) as total
FROM table_a
LEFT OUTER JOIN table_b
ON table_a.id = table_b.user_id
GROUP BY (table_a.id)
) AS tempTable
INNER JOIN table_a
ON tempTable.Id = table_a.Id;
Comparing this with Cybernate's solution, non-standard SQL looks very attractive :-)

Try this:
SELECT a.*, ISNULL(bcnt, 0) bcnt
FROM TableA a LEFT JOIN
(
SELECT user_id, COUNT(1) AS BCNT
FROM TableB
GROUP BY user_id
) b
ON a.id = b.user_id

You can just use a LEFT JOIN and AGGREGATION functions
SELECT b.user_id,
min(a.username) UserName,
min(a.email) Email,
min(a.address) Address,
COUNT(*) Quantity
FROM table_b b left join
table_a a on a.id=b.user_id
group by b.user_id

select
*,
(select count(*)
from #TableB as B
where A.id = B.user_id) as total
from #TableA as A

Related

Returning the first result from another table if statement is true

So I'm struggling to find the logic for the next problem:
I got 2 tables
TABLE A has the following column
Postalcode
1111
2222
3333
4444
TABLE B has the following column
Postalcode
1111AA
1111BB
1111CA
2222AA etc
What I would like to have is that if the Postalcodes first 4 numbers are found from Table A in table B, then I would like to have the first result of that postalcode from Table B (4digits+2letters).
e.g. if the postalcode in A is 1111 and substring(postalcode, 1, 4) of Table B is also 1111, then return the first result of that postalcode from Table B --> 1111AA
I can't seem to find the answer for this and I'm struggling for a while now.
Hope you guys have the solution for me.
If for each record in table A you want to match at most one record from Table B, an OUTER APPLY (SELECT TOP 1 ...) should do the trick.
Try:
select a.PostalCode, b1.Postalcode
from table_a a
outer apply (
select top 1 *
from table_b b
where b.Postalcode LIKE a.Postalcode + '%'
order by b.id
) b1
order by a.PostalCode;
If you wish to omit results that have no matching table_b record, change the OUTER APPLY to a CROSS APPLY. A OUTER APPLY is like a LEFT JOIN while a CROSS APPLY is like an INNER JOIN.
See this db<>fiddle fr a demo.
(Credit Bernd Buffen for the data setup. Note that I changed PostalCode from INT to VARCHAR to simplify the match criteria.)
i have change the sample from #Ergest Basha with a virtual column and index
CREATE TABLE table_a (
Postalcode INT ,
KEY idx_sPortalcode (Postalcode)
);
INSERT INTO table_a VALUES
(1111),
(2222),
(3333),
(4444);
CREATE TABLE table_b (
id INT,
Postalcode VARCHAR(25),
sPostalcode INT AS ( 0 + Postalcode) STORED,
KEY idx_sPortalcode (sPostalcode)
);
INSERT INTO table_b (id,Postalcode) VALUES
(1,'1111AA'),
(2,'1111BB'),
(3,'1111CA'),
(4,'2222AA');
SELECT * FROM table_b;
-- EXPLAIN
SELECT b.Postalcode
FROM table_a a
INNER JOIN table_b b ON b.sPostalcode=a.Postalcode
WHERE a.Postalcode=1111
ORDER BY b.id ASC LIMIT 1;
Something like this: MySQL
select b.Postalcode
from table_a a
inner join table_b b on LEFT(b.Postalcode,4)=a.Postalcode
where a.Postalcode=1111
order by b.id asc limit 1;
Check the demo
SQL Server
select top(1) b.Postalcode
from table_a a
inner join table_b b on LEFT(b.Postalcode,4)=a.Postalcode
where a.Postalcode=1111
order by b.id ;
Demo
Edit based on comments*
I think you need something like below, but check #Bernd Buffen suggestion for performance:
WITH cte AS (
SELECT Postalcode, ROW_NUMBER() OVER ( PARTITION BY LEFT(Postalcode,4) ORDER BY id asc ) row_num
FROM table_b
)
SELECT cte.Postalcode
FROM table_a a
INNER JOIN cte on LEFT(cte.Postalcode,4)=a.Postalcode
WHERE row_num = 1 ;
Demo

How can I show all information in a table and count how many times its shown up in another table?

I am working in SQLite3 where I am trying to use a SELECT statement to show the entire detail of a table and then count how many times it's appeared in another table.
For example: I have 2 tables, A_ID being a foreign key to A and ID being the primary key for Table A
Table A : ID | Name -> info (1,Sam), (2, Michael), (3,Gordon)
Table B : A_ID | Task -> info (1, T1), (1, T2), (2, T3), (3, T4)
OUTPUT: ID | NAME | COUNT() -> info (1 | Sam | 2), (2 | Michael | 1), (3 | Gordon | 1)
I had thought to try
SELECT \*, COUNT(*)
FROM A
WHERE ID = (SELECT A_ID FROM B);
But this statement only showed me the first item and not the rest.
Sorry about the formatting, I'm not too familiar with using this yet. Thank you
You need to use a GROUP BY clause to get the values for each person in A, and LEFT JOIN table A to B on A_ID to get the counts of tasks for each person:
SELECT A.ID, A.Name, COUNT(B.Task) AS Tasks
FROM A
LEFT JOIN B ON B.A_ID = A.ID
GROUP BY A.ID, A.Name
Output (for your sample data):
ID Name Tasks
1 Sam 2
2 Michael 1
3 Gordon 1
Demo on dbfiddle
You should use GROUP BY statement with aggregating functions:
SELECT Name, COUNT(task)
FROM A left join B on A.ID=B.A_ID
GROUP BY Name;
You need a left join of TableA to TableB and group by id and name:
select a.id, a.name, count(b.a_id) counter
from TableA a left join TableB b
on b.a_id = a.id
group by a.id, a.name
For performance, I recommend a correlated subquery:
SELECT a.*, (SELECT COUNT(*) FROM B WHERE B.A_ID = A.ID)
FROM A;
This can take advantage of an index on B(A_ID) and avoids the outer aggregation.

SQL select newst data in 2 table same primary key

I have two same table:
A(id, ..., modified_date)
and B(id, ..., modified_date). I need to select the record with same id but modified_date larger.
How can I write the SQL? Please help.
Example:
Table A
id | user name | email | modified date
------------------------------------------------
1 | Anne | ana#gmail.com | 2016/12/20
And table B
id | user name | email | modified date
------------------------------------------------
1 | Anne Jr, | ana_j#gmail.com | 2017/01/20
With two record has same id, I need to get the record with modified_date larger. The example above, with id = 1, I need to select the record has modified_date = 2017/01/20
You can do a JOIN and then ORDER BY modified_date column like
select t1.id,t1.modified_date
from table1 t1 join table2 t2 on t1.id = t2.id
order by t1.modified_date desc;
If you need data from the B table, you can use :
SELECT b.*
FROM B b
WHERE b.id = a.id
AND b.modified_date > a.modified.date
Similarly, if you need data from the A table you can use :
SELECT a.*
FROM A a
WHERE a.id = b.id
AND a.modified_date > b.modified.date
In case there are multiple records which fit the criteria and you need only the one record which has the greatest modified date value then you can use :
SELECT TOP 1 a.*
FROM A a
WHERE a.id = b.id
AND a.modified_date > b.modified.date
ORDER BY a.modified_date
OR
SELECT TOP 1 b.*
FROM B b
WHERE b.id = a.id
AND b.modified_date > a.modified.date
ORDER BY b.modified_date
Hope this helps!!!
You can try using a CASE expression on SQL Server
SELECT A.id,A.other_columns,
(CASE WHEN a.modified_date > b.modified_date THEN a.modified_date ELSE b.modified_date END) as modified_date
FROM [A] INNER JOIN [B] on A.id=B.id
If you want the higher of those two values, then use greatest() with a join:
select ta.id,
greatest(ta.modifed_date, tb.modified_date)
from table_a ta
join table_b tb on ta.id = tb.id;
If you want all columns from the row with the later date, you can use a case statement:
select ta.id,
case
when ta.modified_date > tb.modified_date then ta.email
else tb.email
end as email,
case
when ta.modifed_date > tb.modified_date then ta.user_name
else tb.user_name
end as user_name,
greatest(ta.modified_date, tb.modified_date) as modified_date
from table_a ta
join table_b tb on ta.id = tb.id;

Need to retrieve all records in table A and only single one in table B that is the last updated

I have to retrieve certain records in TABLE_A - then need to display the last time the row was updated - which is in TABLE_B (however, there are many records that correlate in TABLE_B). TABLE_A's TABLE_A.PK is ID and links to TABLE_B through TABLE_B.LINK, where the schema would be:
TABLE_A
===================
ID NUMBER
DESC VARCHAR2
TABLE_B
===================
ID NUMBER
LINK NUMBER
LAST_DATE DATE
And the actual table data would be:
TABLE_A
===================
100 DESCRIPTION0
101 DESCRIPTION1
TABLE_B
===================
1 100 12/12/2012
2 100 12/13/2012
3 100 12/14/2013
4 101 12/12/2012
5 101 12/13/2012
6 101 12/14/2013
So, I would need something to read out:
Result
====================
100 DESCRIPTION0 12/14/2013
101 DESCRIPTION1 12/14/2013
I tried to join different ways, but nothing seems to work:
select * from
(SELECT ID, DESC from TABLE_A WHERE ID >= 100) TBL_A
full outer join
(select LAST_DATE from TABLE_B WHERE ROWNUM = 1 order by LAST_DATE DESC) TBL_B
on TBL_A.ID = TBL_B.LINK;
The easiest thing to do would be to join table_a with an aggregate query on table_b:
SELECT table_a.*, table_b.last_date
FROM table_a
LEFT JOIN (SELECT link, MAX(last_date) AS last_date
FROM table_b
GROUP BY link) table_b ON table_a.id = table_b.link
If you just want the most recent date, think aggregation and join. The extra levels of subqueries do not help. Something like:
select a.id, a.desc, max(last_date)
from table_a a join
table_b b
on a.id = b.link
where a.id >= 100
group by a.id, a.desc;
Note: I doubt a full outer join is necessary, although you can keep that if you have join keys that don't match between the tables. Perhaps a left join is appropriate.
I should point out that if you want more fields from b, then your initial inclination to use row_number() is correct. But the query would look like:
select a.id, a.desc, max(last_date)
from table_a a left join
(select b.*, row_number() over (partition by link order by last_date desc) as seqnum
from table_b b
) b
on a.id = b.link and b.seqnum = 1
where a.id >= 100
group by a.id, a.desc;

SQL: select all unique values in table A which are not in table B

I have table A
Id | Name | Department
-----------------------------
0 | Alice | 1
0 | Alice | 2
1 | Bob | 1
and table B
Id | Name
-------------
0 | Alice
I want to select all unique Ids in table A which do not exist in table B. how can I do this?
select distinct id
from TableA a
where not exists (
select id
from TableB
where id = a.id
)
Just to provide a different solution than NOT IN :
SELECT DISTINCT A.Id
FROM A
LEFT OUTER JOIN B
ON A.Id = B.Id
WHERE B.Id IS NULL
The "good" solution is usually MINUS or EXCEPT, but MySQL doesn't support it.
This question was asked a few time ago and someone posted an article comparing NOT IN, NOT EXISTS and LEFT OUTER JOIN ... IS NULL. It would be interesting if someone could find it again!
The most efficient answer is to use a left join, as using "NOT IN" can sometimes prevent a query from using an index, if present.
The answer in this case would be something like
SELECT DISTINCT
*
FROM
TableA a
LEFT JOIN
TableB b
ON
a.Id = b.Id
WHERE
b.Id IS NULL
Alternatively, this is more readable than a left join, and more efficient than the NOT IN solutions
SELECT * FROM TableA a where NOT EXISTS (SELECT * FROM TableB where Id = a.Id)
I'd use a NOT EXISTS Like this:
SELECT A.Id
FROM TableA A
WHERE NOT EXISTS (SELECT B.Id FROM TableB B WHERE A.Id = B.Id)
GROUP BY A.Id
SELECT DISTINCT Id FROM A WHERE Id NOT IN (SELECT ID FROM B);
SELECT DISTINCT Id FROM A WHERE Id NOT IN(SELECT DISTINCT Id FROM B);
The subquery will get all the IDs in B. The group by...having will get all the unique IDs in A that are also not in B.
select *
from A
where id not in
(
select distinct id
from B
)
group by ID
having count(*) > 1;