SQL Find value in different tables from generic table - sql

I have 3 tables
Cats:
| Id | Name |
|----|-------|
| 1| Cat1|
| 2| Cat2|
| 3| Cat3|
Dogs:
| Id | Name |
|----|-------|
| 1 | Dog1|
| 2 | Dog2|
| 3 | Dog3|
Owner:
| Id | Name |TableName |EntityId|
|----|-------|--------------------
| 1 | John| Dog | 1|
| 2 | Pete| Cat | 1|
| 3 |Jessica| Cat | 2|
I have no control over the animal tables, I mean, may be N animal tables, cats and dogs was just an example.
There is a way to get the names of cats and dogs in one select query?
The result must be
| OwnerId | Name |TableName |EntityId|AnimalName|
|---------|-------|-------------------------------
| 1 | John| Dog | 1| Dog1 |
| 2 | Pete| Cat | 1| Cat1 |
| 3 |Jessica| Cat | 2| Cat2 |

You can use a case statement for this:
select o.id, o.name,
case when o.tablename = 'dogs' then d.name else c.name end name
from owner o
left join cats c on o.entityid = c.id and o.tablename = 'cats'
left join dogs d on o.entityid = d.id and o.tablename = 'dogs'
SQL Fiddle Demo
As others have mentioned, if you have the opportunity to update your data model, then this could be a lot easier. Have a Pet table, an Owner table and an OwnerPet table (x-ref table). Then it's a simple query with inner join.

If I were you, I would modify your table schema to something like:
PetType
PetTypeId
PetTypeName
Pet
PetId
PetTypeId
PetName
Owner
OwnerId
OwnerName
OwnerPet
OwnerId
PetId
Then you could do something like this:
select *
from Owner
join OwnerPet on OwnerPet.OwnerId = Owner.OwnerId
join Pet on OwnerPet.PetId = Pet.PetId
join PetType on Pet.PetTypeId = PetType.PetTypeId
SQL Fiddle Example

You can use either left join or union to accomplish this task, both methods are mentioned below
SELECT
OW.ID,OW.Name,OW.TableName,OW.EntityId,
CASE WHEN OW.TableName='Cat' THEN CA.Name ELSE DO.Name END AnimalName
FROM Owner OW
LEFT JOIN Cats CA ON OW.EntityId=CA.ID AND OW.TableName='Cat'
LEFT JOIN Dogs DO ON OW.EntityId=DO.ID AND OW.TableName='Dog'
SELECT
OW.ID,OW.Name,OW.TableName,OW.EntityId,CA.Name AnimalName
FROM Owner OW
JOIN Cats CA ON OW.EntityId=CA.ID AND OW.TableName='Cat'
UNION
SELECT
OW.ID,OW.Name,OW.TableName,OW.EntityId,DO.Name AnimalName
FROM Owner OW
JOIN Dogs DO ON OW.EntityId=DO.ID AND OW.TableName='Dog'

Related

Return list of tables and count in single query

I know about the describe command \d and select count(*) from my_schema_1.my_table_1;. However I'd like to get a neat list of the entire database, I have quite a few tables. Something like below would be nice.
my_schema_1 | mytable_1 | 12323
my_schema_2 | mytable_2 | 0
I'd basically like to loop over all the tables.
Maybe something like this (no need to execute a COUNT(*)) for each table):
EDIT new version to consider tables without projections:
SELECT
t.table_schema AS schema,
t.table_name AS table,
ZEROIFNULL(
CASE WHEN p.is_segmented IS TRUE
THEN SUM(ps.row_count) * COUNT(DISTINCT ps.node_name) // COUNT(ps.node_name)
ELSE MAX(ps.row_count)
END
) AS row_count,
CASE WHEN p.is_segmented THEN 'Yes' ELSE 'No' END AS segmented,
COUNT(DISTINCT p.projection_id) AS num_proj
FROM
v_catalog.tables t
LEFT OUTER JOIN v_monitor.projection_storage ps
ON t.table_id = ps.anchor_table_id
LEFT OUTER JOIN v_catalog.projections p
ON t.table_id = p.anchor_table_id
AND p.is_super_projection IS TRUE
GROUP BY
t.table_schema, t.table_name, p.is_segmented
ORDER BY
t.table_schema, t.table_name
;
Sample output:
schema | table | row_count | segmented | num_proj
--------+------------------------+-----------+-----------+----------
mauro | city | 5 | Yes | 2
mauro | employees | 1000000 | Yes | 2
mauro | empty | 0 | No | 0
mauro | fnames | 20 | Yes | 2
...
tpch | customer | 0 | Yes | 2
tpch | lineitem | 54010935 | Yes | 2
tpch | nation | 25 | No | 1
tpch | orders | 718277000 | Yes | 2
I did add a couple of columns: segmented (Yes/No) and num_proj. You can remove them if you want.

Use JOIN on multiple columns multiple times

I am trying to figure out the best way to use a JOIN in MSSQL in order to do the following:
I have two tables. One table contains technician IDs and an example of one data set would be as follows:
+--------+---------+---------+---------+---------+
| tagid | techBid | techPid | techFid | techMid |
+--------+---------+---------+---------+---------+
| 1-1001 | 12 | 0 | 11 | 6 |
+--------+---------+---------+---------+---------+
I have another table that stores the names of these technicians:
+------+-----------+
| TTID | SHORTNAME |
+------+-----------+
| 11 | Steven |
| 12 | Mark |
| 6 | Pierce |
+------+-----------+
If the ID of a technician in the first table is 0, there is no technician of that type for that row (types are either B, P, F, or M).
I am trying to come up with a query that will give me a result that contains all of the data from table 1 along with the shortnames from table 2 IF there is a matching ID, so the result would look something like the following:
+--------+---------+---------+---------+---------+----------------+----------------+----------------+----------------+
| tagid | techBid | techPid | techFid | techMid | techBShortName | techPShortName | techFShortName | techMShortName |
+--------+---------+---------+---------+---------+----------------+----------------+----------------+----------------+
| 1-1001 | 12 | 0 | 11 | 6 | Mark | NULL | Steven | Pierce |
+--------+---------+---------+---------+---------+----------------+----------------+----------------+----------------+
I am trying to use a JOIN to do this, but I cannot figure out how to join on multiple columns multiple times to where it would look something like
Select table1.tagid, table1.techBid, table1.techPid, table1.techFid, table1.techMid, table2.shortname
FROM table1
INNER JOIN table2 on //Dont know what to put here
You need to use left joins like this:
Select table1.tagid, table1.techBid, table1.techPid, table1.techFid, table1.techMid,
t2b.shortname, t2p.shortname, t2f.shortname, t2m.shortname,
FROM table1
LEFT JOIN table2 t2b on table1.techBid = t2b.ttid
LEFT JOIN table2 t2p on table1.techPid = t2p.ttid
LEFT JOIN table2 t2f on table1.techFid = t2f.ttid
LEFT JOIN table2 t2m on table1.techMid = t2m.ttid
you just do mutiple left join
select tech.techPid, techPname.SHORTNAME
, tech.techFid, techFname.SHORTNAME
from tech
left join techName as techPname
on tech.techPid = techPname.TTID
left join techName as techFname
on tech.techFid = techFname.TTID

SQL resulting table satisfies two conditions

I hope I can explain this well enough.
Say I have this table:
Owner
+--------+--------+
| Name | Type |
+--------+--------+
| Bob | Cat |
| Bob | Dog |
| Bob | Cow |
| Tim | Dog |
| Tim | Cat |
| Ted | Cat |
| Joe | Dog |
| Joe | Cat |
| Joe | Sheep |
+--------+--------+
I am trying to find everyone who has all the animals tim has (so a cat and a dog). This means Joe and Bob would satisfy this, but not Ted as he only has one type of animal Tim has
How would I go about getting this result?
So I have a table with all the types tim owns:
SELECT Type FROM Owner WHERE Name= 'Tim';
How do I get it so that only those who have both Types tim has get selected from the list of owners?
Any guidance would be appreciated, thanks in advance.
select name
from owner
where type in (select distinct type from owner where name = 'tim')
group by name
having count(distinct type) = (select count(distinct type) from owner where name = 'tim')
I think of this as a join and group by problem. Join Tim's records to all the other owners, but on the type field. Then do an aggregation and keep only the records where all the types match:
select o.name
from owner otim left join
owner o
on o.type = tim.type and o.name <> 'Tim' and otim.name = 'Tim'
group by o.name
having min(case when o.type is null then 0 else 1 end) = 0;
Note that this works even when duplicate type values are allowed in the table.
If you are just trying to get all the persons who have more animals than Tim has then you can do it like
select Name from owners
group by Name
having count(distinct [Type]) > (select count(distinct type) from owners
where Name='tim')

SQL Server - Given a Set of Columns, finding missing combinations within the Set

I have the following Table. What I want to get is the missing combinations of Student, Class, Book. I have a query below that does it, but I would like others to provide more efficient queries (ie possibly ones that use group by) to find the missing combos.
SQL FIDDLE HERE - http://sqlfiddle.com/#!6/16e2b/3
StudentBook Table
+---------+---------+--------------+
| Student | Class | Book |
+---------+---------+--------------+
| Albert | Math | AlgebraBook |
| Albert | Math | FractionBook |
| Bridget | Math | AlgebraBook |
| Bridget | Math | FractionBook |
| Charles | Math | AlgebraBook |
| Charles | Math | FractionBook |
| Debbie | English | NovelBook |
| Debbie | English | PoemBook |
| Edward | English | PoemBook |
| Frank | English | PoemBook |
+---------+---------+--------------+
The following Rows in the Set are the missing combinations
Correct Result of My Query Below
+---------+---------+-----------+
| Student | Class | Book |
+---------+---------+-----------+
| Edward | English | NovelBook |
| Frank | English | NovelBook |
+---------+---------+-----------+
And I can use the following Query to get the Missing Combinations, but I want a faster more efficient solutions. Basically I'm looking for other more Effective Techniques, such as possibly using Group By.
WITH CTE_ClassBooks AS
(
SELECT DISTINCT Class, Book FROM StudentBook
),
CTE_StudentClasses AS
(
SELECT DISTINCT Student, Class FROM StudentBook
),
CTE_CombosOfStudentClassBooks AS
(
SELECT DISTINCT b.Student, a.Class, a.Book
FROM CTE_ClassBooks a
INNER JOIN CTE_StudentClasses b ON a.Class = B.Class
)
SELECT * FROM CTE_CombosOfStudentClassBooks
EXCEPT
SELECT * FROM StudentBook
This might be a little faster, your route doesn't seem terribly inefficient though.
;WITH cte AS (SELECT DISTINCT Class,Book FROM Table1)
SELECT b.Student,a.*
FROM cte a
JOIN Table1 b
ON a.Class = b.Class
LEFT JOIN Table1 c
ON a.Class = c.CLass
AND a.Book = c.Book
AND b.Student = c.Student
WHERE c.Class IS NULL
Demo: SQL Fiddle
SELECT S1.STUDENT,S1.CLASS,S2.BOOK FROM
STUDENTBOOK S1,(SELECT DISTINCT CLASS,BOOK FROM STUDENTBOOK) S2
WHERE S1.CLASS = S2.CLASS
AND S1.BOOK <> S2.BOOK
EXCEPT
SELECT STUDENT,CLASS,BOOK FROM STUDENTBOOK

How to COUNT a field not directly related to the where expression on ORACLE

I am trying to find out How many students are enrolled in all units, that are associated with a particular course.
I have tried.
SELECT COUNT(studentID) AS Expr1
FROM Course CROSS JOIN
Enrolment
WHERE (Course.courseCode = 'S4000')
But i am getting 3 , each time for the different course codes.
It should be 1 for S4000. I have 3 students in total.
You didn't tell us columns names, so I create:
SELECT cu.courseCode, COUNT(e.StudentID) AS tot_Students
FROM COURSEUNIT cu INNER JOIN ENROLMENT e
ON cu.unitCode = e.couseCode
GROUP BY cu.courseCode
Its because of what happens in cross join each record in 1st table is get related other record 2nd table .in your case suppose there are 3 student and 3 courses
Student table
Id | Name
1 | abc
2 | pqr
3 | xyz
Courses
Code | Name
S4000 | c1
S4001 | c2
S4002 | c3
Cross join table
Student.Id | Student.Name | Courses.code |courses.name
1| abc| S4000 | c1
2 | pqr| S4000 | c1
3 | xyz| S4000 | c1
1| abc| S4001 | c2
2 | pqr| S4001 | c2
3 | xyz| S4001 | c2
1| abc| S4002 | c3
2 | pqr| S4002 | c3
3 | xyz| S4002 | c3
Now you can see their are 3 records created for each courses so you are getting 3 answer each time.
so in your query you should have same common field to get required records such as there will Fourier key relationship
you add common field in where clause like
SELECT COUNT(studentID) AS Expr1
FROM Course CROSS JOIN
Enrolment
WHERE (Course.courseCode = 'S4000')
and Course.studentID=Enrolment.studentID
OR you can use inner join on common field