Combining sql select and Count - sql

I have two tables
A and B
A B
----------------- -----------------
a_pk (int) b_pk (int)
a_name(varchar) a_pk (int)
b_name (varchar)
I could write a query
SELECT a.a_name, b.b_name
FROM a LEFT OUTER JOIN b ON a.a_pk = b.a_pk
and this would return me a non distinct list of everything in table a and its table b joined data. Duplicates would display for column a where different b records shared a common a_pk column value.
But what I want to do is get a full list of values from table A column a_name and ADD a column that is a COUNT of the joined values of table B.
So if a_pk = 1 and a_name = test and in table b there are 5 records that have a a_pk value of 1 my result set would be
a_name b_count
------ -------
test 5

The query should like this :
SELECT
a.a_name,
(
SELECT Count(b.b_pk)
FROM b
Where b.a_pk = a.a_pk
) as b_count
FROM a

SELECT a_name, COUNT(*) as 'b_count'
FROM
A a
JOIN B b
ON a.a_pk = b.a_pk
GROUP BY a_name

SELECT
a.name,
(
SELECT COUNT(1)
FROM B b
WHERE b.a_pk = a.a_pk
)
FROM A a

Related

Postgres Relationship query

I have two tables A and B where the relationship is one to many (A -> many B). Table "A" contains columns id, name and table "B" has id, a_id(fk), is_off(boolean).
Now, I want to get id of "A" which has all "B"'s is_off = true.
I tried this one select a.id from A a inner join B b on a.id = b.a_id where b.is_off = true But it only returns even if an "A" has an item (B) which has is_off = false;
Any help will be appreciated.
You were close. A subquery is probably what you're looking for:
Test data
CREATE TABLE a (id int PRIMARY KEY, name text);
CREATE TABLE b (id int, a_id int REFERENCES a(id), is_off boolean);
INSERT INTO a VALUES (1,'foo');
INSERT INTO b VALUES (42,1,true),(1,1,false);
Your query would return all records if at least one of the b records fulfil your join and where clauses:
SELECT * FROM a
JOIN b on a.id = b.a_id
WHERE b.is_off;
id | name | id | a_id | is_off
----+------+----+------+--------
1 | foo | 42 | 1 | t
(1 Zeile)
If you intend to exclude all a records that contain at least one is_off = true, you can use NOT IN with a subquery, but as suggested by #a_horse_with_no_name (see comments below) you could use EXISTS or NOT EXISTS:
SELECT * FROM a
WHERE NOT EXISTS (SELECT a_id FROM b WHERE a.id = b.a_id AND is_off);
id | name
----+------
(0 Zeilen)

Optional on condition in SQL Server

I want to update a table based on another table.
I want to update table b using table A. One Id can have multiple serial number in table B. If I want to update all the serial number for a ID then I will pass null in table A, If I want to update only particular serial number then I will pass that serial number. So Serial number is like optional. How to achieve this?
This covers all cases:
SELECT B.ID, B.SerialNumber
FROM TableA A
JOIN TableB B ON A.ID=B.ID AND (A.SerialNumber=B.SerialNumber OR A.SerialNumber IS NULL)
Demo:
WITH TableA AS
(
SELECT * FROM (VALUES
(1,'AB'),
(1,'BC'),
(2,NULL),
(3,'AB')
)T(ID,SerialNumber)
), TableB AS
(
SELECT * FROM (VALUES
(1,'AB'),
(1,'BC'),
(2,'AB'),
(2,'BC'),
(3,'AB'),
(3,'BC'),
(3,'DE')
)T(ID,SerialNumber)
)
SELECT B.ID, B.SerialNumber
FROM TableA A
JOIN TableB B ON A.ID=B.ID AND (A.SerialNumber=B.SerialNumber OR A.SerialNumber IS NULL)
Result
ID SerialNumber
----------- ------------
1 AB
1 BC
2 AB
2 BC
3 AB
You can join both tables :
SELECT [A].[ID]
,[B].[SerialNumber]
,[A].[Values]
FROM [TableA] A
LEFT JOIN [TableB] B ON ([B].[ID] = [A].[ID] AND ([B].[SerialNumber] = [A].[SerialNumber] OR [A].[SerialNumber] IS NULL))

SQL: pivot multiple tables

I have two tables
TableA
ID Qualification
1 A
2 A
3 B
TableB
ID Qualification
1 C
2 A
3 A
Unfortunately, the names of the columns in table A and B are the same - resulting in an error 8156 - The column 'Qualification' was specified multiple times.
My select looks like follows
SELECT *
FROM (
SELECT A.ID, A.Qualification, B.Qualification
FROM TableA A LEFT OUTER JOIN TableB B
ON A.ID = B.ID
)s
PIVOT
(SUM(ID)
FOR Qualification IN ([A],[B],[C])) pvt
TIA!

Find differences between two large tables in oracle

I have two different tables, say table A and B in oracle with around 15 million records in each. Table A has columns (a,b,c,d) and
Table B has columns (e,f,g,h).
The objective is to write a stored procedure to check if every record present in table A is also present in table B and vice versa. Differences between these two should be inserted into a third table.
My problem is that
column a in Table A should be compared with concatenate of column e and f in table B if column e contains a certain string (0311),
if not I have to compare it with just column f.
Column b should be compared with column g in table B and
I also have to compare column c in the table A with column g in table B, if the two aren't a match column d should be compared with column g.
What's the fastest way to do so?
for example these two are a match:
Table A: 9353456789,03117884657,12082200003035,12082123595535
Table B: 9353456789,0311,7884657,12082200003035
or:
Table A: 9353456789,03117884657,12082200003035,12082123595535
Table B: 9353456789,0311,7884657,12082123595535
example of records that do not need concatenation and are a match:
Table A: 9353456789,03617884657,12082200003035,12082123595535
Table B: 9353456789,0361,03617884657,12082200003035
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE TableA ( a VARCHAR2(20), b VARCHAR2(20), c VARCHAR2(20), d VARCHAR2(20) );
CREATE TABLE TableB ( e VARCHAR2(20), f VARCHAR2(20), g VARCHAR2(20), h VARCHAR2(20) );
CREATE TABLE TableC ( i VARCHAR2(20), j VARCHAR2(20), k VARCHAR2(20), l VARCHAR2(20) );
INSERT INTO TableA
SELECT '9353456789','03117884657','12082200003035','12082123595535' FROM DUAL
UNION ALL SELECT '9353456789','03617884657','12082200003035','12082123595535' FROM DUAL
UNION ALL SELECT '9353456789','03617884657','12082200003034','12082123595534' FROM DUAL;
INSERT INTO TableB
SELECT '9353456789','0311','7884657','12082200003035' FROM DUAL
UNION ALL SELECT '9353456789','0311','7884657','12082123595535' FROM DUAL
UNION ALL SELECT '9353456789','0361','03617884657','12082200003035' FROM DUAL
UNION ALL SELECT '9353456789','0361','03617884657','12082200003036' FROM DUAL;
Query 1:
To insert the rows - perform an INSERT INTO... SELECT using a FULL OUTER JOIN between both tables using your requirements as the join condition; then for the rows which do not match either TableA(a, b, c, d) will all be NULL or TableB(e, f, g, h) will all be NULL and this can be used in the WHERE condition to only get the non-matched rows. Finally, so as not to return NULL values, COALESCE() is used for the returned values.
INSERT INTO TableC
SELECT COALESCE( ta.a, tb.e ) AS i,
COALESCE( ta.b, tb.f ) AS j,
COALESCE( ta.c, tb.g ) AS k,
COALESCE( ta.d, tb.h ) AS l
FROM TableA ta
FULL OUTER JOIN
TableB tb
ON ( ta.a = tb.e
AND ta.b = CASE tb.f WHEN '0311' THEN tb.f || tb.g ELSE tb.g END
AND ( ta.c = tb.h OR ta.d = tb.h )
)
WHERE ta.a IS NULL
OR tb.e IS NULL;
Query 2:
SELECT * FROM TableC
Results:
| I | J | K | L |
|------------|-------------|----------------|----------------|
| 9353456789 | 03617884657 | 12082200003034 | 12082123595534 |
| 9353456789 | 0361 | 03617884657 | 12082200003036 |
I'd do this as two statements, though it can be combined
Select a.*
from tablea a left join tableb b on a.a =
case when e = 'string' then b.e || b.f else b.f end
and ...
where b.e is null
The left join will return nulls where a row isn't found in table b, so this should bring up a list of rows i9n table a not in table b. Change the statement to a right join and select b.* and you'll see whats in b but not in a.
Statement can be turned into a 'create table as' which will create a new table with the results from this select statement.
I put and ... your conditions there are a bit confusing, you'll just need to use case statements to pick which columns you want to compare/join on.

CTE to recurse to root in Table A, and join results from Table B at each iteration

I'm new to CTE's in T-SQL but am loving them. However, I cannot get the logic right for this particular stored procedure I'm writing.
Given Table A with the columns:
Id Name Inherits ...
Where the column Inherits stores an int that is an id to another row in this same table.
And Table B with the columns:
Id Name AId ...
Where AId is a foreign key to a row in Table A.
How could one use a CTE to start from an arbitrary row (x) in A, collect all rows in B where AId = x.Id, and then recurse upwards in A by setting x to the row pointed to by x.Inherits. This should proceed until x.Inherits IS NULL.
So the overall effect is I want to return the related B rows for the starting A Id, and then all inherited B rows that we discover by examining the Inherits column of A recursively.
My thinking is to set up the CTE to recurse to the 'root' of A from an arbitrary row, and inside each recursive call have a JOIN to B. This is as far as I've got:
WITH c
AS
(
SELECT A.Inherits,A.Id, B.Name, 1 AS Depth
FROM tbl_A A
INNER JOIN tbl_B B ON B.AId = A.Id
WHERE A.Id = #ArbitraryStartingAId
UNION ALL
SELECT T.Inherits,T.Id, c.Name, c.Depth + 1 AS 'Level'
FROM tbl_A T
INNER JOIN c ON T.Id = c.Inherits
)
SELECT *
FROM c
Which produces:
1 4 b_val 1
11 1 b_val 2
NULL 11 b_val 3
Where b_val is taken from a Table B row (y) where #ArbitraryStartingAId = y.AId. The recursion on A is working, but not pulling in the correct corresponding B data upon each iteration.
If someone could help rewrite this to yield the results I need then that would be great.
Many thanks
Supposing we have
CREATE TABLE A ( Id int, Name nvarchar(10), Inherits int );
CREATE TABLE B ( Id int, Name nvarchar(10), AId int );
And some data:
INSERT A VALUES ( 1, 'one', 2 );
INSERT A VALUES ( 2, 'two', 3 );
INSERT A VALUES ( 3, 'three', null );
INSERT A VALUES ( 4, 'four', 3 );
INSERT B VALUES ( 1, 'B one', 1 );
INSERT B VALUES ( 2, 'B two', 2 );
INSERT B VALUES ( 3, 'B three', 3 );
INSERT B VALUES ( 4, 'B four', 4 );
We can build a CTE on A, and then join to B:
WITH cteA AS
(
-- Anchor
SELECT Id, Name, Inherits FROM A
WHERE Id = 1
UNION
-- Recursive
SELECT A.Id, A.Name, A.Inherits FROM A
INNER JOIN cteA ON A.Id = cteA.Inherits
)
SELECT * FROM cteA INNER JOIN B ON cteA.Id = B.AId;
to get (with the anchor at 1):
Id Name Inherits Id Name AId
----------- ---------- ----------- ----------- ---------- -----------
1 one 2 1 B one 1
2 two 3 2 B two 2
3 three NULL 3 B three 3
Here's where I'm at now and it's one possible answer as the correct data is returned:
WITH c
AS
(
SELECT A.Inherits,A.Id, B.Name, 1 AS Depth
FROM tbl_A A
INNER JOIN tbl_B B ON B.AId = A.Id
WHERE A.Id =#ArbitraryStartingAId
UNION ALL
SELECT T.Inherits,T.Id, T.Name, B.Name, c.Depth + 1 AS 'Level'
FROM tbl_A T
INNER JOIN tbl_B B ON B.AId = T.Id
INNER JOIN c ON T.Id = c.Inherits
)
SELECT DISTINCT *
FROM c ORDER BY Depth ASC
It feels a bit dirty though as it returns the root level B rows twice, hence the DISTINCT operator to filter these out...??