Select with count on 3 tables - sql

I need your help for a particular SELECT on 3 tables. I'm not skilled on SQL so it's a difficult SELECT for me, since I have to apply COUNT (I suppose) to the query.
I show you my tables:
I need to know how many contacts there are in the database (all the contacts!!!!) and how many photos and videos are bound to any contact.
I should get a result similar to this:
-----------------------------------
| ID | NAME | PHOTO | VIDEO |
-----------------------------------
| 1 | MARK | 3 | 1 |
-----------------------------------
| ID | LISA | 2 | 0 |
-----------------------------------
Thank you for your help

You can use the following approach, if you are hesitant about duplicates in the query you can use a sql function and pass type parameter as a string. If you have uncertain number of types (VIDEO, PHOTO, TEXT etc) you need to redesign the output table format (I would go with the following tuple TYPE, CONTACT_ID, COUNT), or at the worst case go with dynamic query construction.
select c.ID, c.NAME,
(select count(*) from CONTACT_MEDIA cm join MEDIA m on
m.ID = cm.ID_MEDIA and m.TYPE = 'PHOTO' where cm.ID_CONTACT = c.ID) as PHOTO,
(select count(*) from CONTACT_MEDIA cm join MEDIA m on
m.ID = cm.ID_MEDIA and m.TYPE = 'VIDEO' where cm.ID_CONTACT = c.ID) as VIDEO
from CONTACT c

Please use below query , this will give you exact result
select contact_media.ID_Contact, contact.Name, count(M1.ID) as 'PHOTO', COUNT(M2.ID) as 'VIDEO' from Contact inner join contact_media on Contact.ID=contact_media.ID_Contact
left outer join media M1 on contact_media.ID_Media=M1.ID and M1.TYPE='PHOTO'
left outer join media M2 on contact_media.ID_Media=M2.ID and M2.TYPE='VIDEO'
group by contact_media.ID_Contact, contact.Name

Related

How to fix SELECT statement to return duplicates

Currently I am trying to return a three table join to find duplicate track titles that are in my a "track" table, and also return the track number | cd title from the other joined tables. My select statement is currently returning all the information from the joined tables but its not showing only the duplicates.
I have also tried using a group by and having clause to try to find a COUNT of comptitle. When I tried that it returned the an empty table.
My Tables:
CREATE TABLE composition (compid, comptitle,...,
PRIMARY KEY (compid),...);
CREATE TABLE recording (rcdid,..., compid,
PRIMARY KEY (rcdid, compid),...);
CREATE TABLE cd (cdid, cdtitle,...,
PRIMARY KEY(cdid),...);
CREATE TABLE track (cdid, trknum,..., rcdid, compid,
PRIMARY KEY (cdid, trknum),...);
My statement:
SELECT comptitle, trknum, cdtitle
FROM track JOIN recording ON track.rcdid = recording.rcdid
JOIN composition ON recording.compid = composition.compid
JOIN cd ON cd.cdid = track.cdid;
Output expected | actual:
EXPECTED:
comptitle | trknum | cdtitle
--------------------------------------------
Cousin Mary | 2 | Giant Steps
Cousin Mary | 10 | Giant Steps
Giant Steps | 1 | Giant Steps
Giant Steps | 8 | Giant Steps
ACTUAL:
comptitle | trknum | cdtitle
----------------------------+--------+-------------
Giant Steps | 8 | Giant Steps
Giant Steps | 1 | Giant Steps
Stomp of King Porter | 1 | Swing
Sing a Study in Brown | 2 | Swing
Cousin Mary | 14 | Swing
Cousin Mary | 10 | Giant Steps
What you need is a subquery to first find the duplicated track titles in track table, then join this to the other tables. This subquery would look like:
SELECT rcdid, COUNT(*) AS number
FROM track
GROUP BY rcdid
HAVING COUNT(*) > 1
Now, depending on what database engine you're using, you may be able to use a CTE which would make it more readable. If that is your case, you could try:
WITH CTE_DuplicatedTracks AS (
SELECT rcdid, COUNT(*) AS number
FROM track
GROUP BY rcdid
HAVING COUNT(*) > 1
)
SELECT
c.comptitle,
t.trknum,
cd.cdtitle
FROM
CTE_DuplicatedTracks dt
JOIN track t ON
dt.rcdid = t.rcdid
JOIN recording r ON
t.rcdid = r.rcdid
JOIN composition c ON
r.compid = c.compid
JOIN cd
ON cd.cdid = t.cdid;
If your engine does not support CTEs:
SELECT
c.comptitle,
t.trknum,
cd.cdtitle
FROM
(
SELECT rcdid, COUNT(*) as number
FROM track
GROUP BY rcdid
HAVING COUNT(*) > 1
) dt
JOIN track t ON
dt.rcdid = t.rcdid
JOIN recording r ON
t.rcdid = r.rcdid
JOIN composition c ON
r.compid = c.compid
JOIN cd
ON cd.cdid = t.cdid;
You can use window functions:
SELECT comptitle, trknum, cdtitle
FROM (SELECT comptitle, trknum, cdtitle,
COUNT(*) OVER (PARTITION BY comptitle) as cnt
FROM track t JOIN
recording r
ON t.rcdid = r.rcdid JOIN
composition c
ON r.compid = c.compid JOIN
cd
ON cd.cdid = t.cdid
) t
WHERE cnt >= 2
ORDER BY cnt, comptitle;

Postgres join and count multiple relational tables

I want to join the 2 tables to the first table and group by a vendor name. I have three tables listed below.
Vendors Table
| id | name
|:-----------|------------:|
| test-id | Vendor Name |
VendorOrders Table
| id | VendorId | Details | isActive(Boolean)| price |
|:-----------|------------:|:------------:| -----------------| --------
| random-id | test-id | Sample test | TRUE | 5000
OrdersIssues Table
| id | VendorOrderId| Details. |
|:-----------|--------------:-----------:|
| order-id | random-id | Sample test|
The expected output is to count how many orders belong to a vendor and how many issues belongs to a vendor order.
I have the below code but it's not giving the right output.
SELECT "vendors"."name" as "vendorName",
COUNT("vendorOrders".id) as allOrders,
COUNT("orderIssues".id) as allIssues
FROM "vendors"
LEFT OUTER JOIN "vendorOrders" ON "vendors".id = "vendorOrders"."vendorId"
LEFT OUTER JOIN "orderIssues" ON "orderIssues"."vendorOrderId" = "vendorOrders"."id"
GROUP BY "vendors".id;```
You need the keyword DISTINCT, at least for allOrders:
SELECT v.name vendorName,
COUNT(DISTINCT vo.id) allOrders,
COUNT(DISTINCT oi.id) allIssues
FROM vendors v
LEFT OUTER JOIN vendorOrders vo ON v.id = vo.vendorId
LEFT OUTER JOIN orderIssues oi ON oi.vendorOrderId = vo.id
GROUP BY v.id, v.name;
Consider using aliases instead of full table names to make the code shorter and more readable.
You are joining along two related dimensions. The overall number of rows is the number of issues. But to get the number of orders, you need a distinct count:
SELECT v.*, count(distinct vo.id) as num_orders,
COUNT(oi.vendororderid) as num_issues
FROM vendors v LEFT JOIN
vendorOrders vo
ON v.id = vo.vendorId LEFT JOIN
orderIssues oi
ON oi.vendorOrderId = vo.id
GROUP BY v.id;
Notes:
Table aliases make the query easier to write and to read.
Quoting column and table names makes the query harder to write and read. Don't quote identifiers (you may need to recreate the tables).
Postgres support SELECT v.* . . . GROUP BY v.id assuming that the id is the primary key (actually, it only needs to be unique). This seems like a reasonable assumption.

SQL Return Column value as Enum - GetValue() field from another Column

Here are the two tables;
Cathegories
----------------------------------------
Cathegory (tinyint) | Name (nvarchar)
----------------------------------------
0 | Field
1 | Mountain
2 | River
----------------------------------------
Places
------------------------------------------
Name (nvarchar) | Cathegory(tinyint)
------------------------------------------
Abc | 2
Xyz | 1
------------------------------------------
When I want to retrieve the Places listing Names and their Cathegories not in the int format but according to the description in Cathegories.
So retrieving Abc I want it like this;
"River" instead the '2'
Please use below query,
select c.name as place, p.name as name from Cathegories c
inner join Places p
on (c.Cathegory = p.Cathegory);
You need to join two table on cathegory as shown below.
select
p.name as places,
c.name as category_name
from places p
join cathegories c
on p.cathegory = c.cathegory
Here is a version of multiple JOIN statements based on the accepted answer:
SELECT
ColumnUserViews,
C.Cathegory AS VCathegory,
ColumnUserPoints,
T.Description AS VTag1,
TT.Description AS VTag2
FROM dbo.Users U
JOIN dbo.Cathegories C ON U.Cathegory = C.Cathegory
JOIN dbo.Tags T ON U.Tag1 = T.Tag
JOIN dbo.Tags TT ON U.Tag2 = TT.Tag
The keyword U defines physical table Users, T Tags, and TT also Tags (free to rename).
VCathegory is a new virtual Column to be retrieved which holds the value of Users.Cathegory translated into Cathegories.Description ('s string equivalent) as per this current scheme.

postgres STRING_AGG() returns duplicates?

I have seen some similar posts, requesting advice for getting distinct results from the query. This can be solved with a subquery, but the column I am aggregating image_name is unique image_name VARCHAR(40) NOT NULL UNIQUE. I don't believe that should be necersarry.
This is the data in the spot_images table
spotdk=# select * from spot_images;
id | user_id | spot_id | image_name
----+---------+---------+--------------------------------------
1 | 1 | 1 | 81198013-e8f8-4baa-aece-6fbda15a0498
2 | 1 | 1 | 21b78e4e-f2e4-4d66-961f-83e5c28d69c5
3 | 1 | 1 | 59834585-8c49-4cdf-95e4-38c437acb3c1
4 | 1 | 1 | 0a42c962-2445-4b3b-97a6-325d344fda4a
(4 rows)
SELECT Round(Avg(ratings.rating), 2) AS rating,
spots.*,
String_agg(spot_images.image_name, ',') AS imageNames
FROM spots
FULL OUTER JOIN ratings
ON ratings.spot_id = spots.id
INNER JOIN spot_images
ON spot_images.spot_id = spots.id
WHERE spots.id = 1
GROUP BY spots.id;
This is the result of the images row:
81198013-e8f8-4baa-aece-6fbda15a0498,
21b78e4e-f2e4-4d66-961f-83e5c28d69c5,
59834585-8c49-4cdf-95e4-38c437acb3c1,
0a42c962-2445-4b3b-97a6-325d344fda4a,
81198013-e8f8-4baa-aece-6fbda15a0498,
21b78e4e-f2e4-4d66-961f-83e5c28d69c5,
59834585-8c49-4cdf-95e4-38c437acb3c1,
0a42c962-2445-4b3b-97a6-325d344fda4a,
81198013-e8f8-4baa-aece-6fbda15a0498,
21b78e4e-f2e4-4d66-961f-83e5c28d69c5,
59834585-8c49-4cdf-95e4-38c437acb3c1,
0a42c962-2445-4b3b-97a6-325d344fda4a
Not with linebreaks, I added them for visibility.
What should I do to retrieve the image_name's one time each?
If you don't want duplicates, use DISTINCT:
String_agg(distinct spot_images.image_name, ',') AS imageNames
Likely, there are several rows in ratings that match the given spot, and several rows in spot_images that match the given sport as well. As a results, rows are getting duplicated.
One option to avoid that is to aggregate in subqueries:
SELECT r.avg_raging
s.*,
si.image_names
FROM spots s
FULL OUTER JOIN (
SELECT spot_id, Round(Avg(ratings.rating), 2) avg_rating
FROM ratings
GROUP BY spot_id
) r ON r.spot_id = s.id
INNER JOIN (
SELECT spot_id, string_agg(spot_images.image_name, ',') image_names
FROM spot_images
GROUP BY spot_id
) si ON si.spot_id = s.id
WHERE s.id = 1
This actually could be more efficient that outer aggregation.
Note: it is hard to tell without seeing your data, but I am unsure that you really need a FULL JOIN here. A LEFT JOIN might actually be what you want.

Select value in another table if reference is found

Alright, I'm pretty desperate. This is my first time using StackOverflow (I've always find all my answer but not this time). Please Community I need your help cause I'm not a SQL wiz. (I'm using Access).
I have 3 tables "User", "Type" and "User_update". I want to get the value from the "User" table but if there's an update for that user in the "User_update" table I would like to have the value from the "User_update" table instead and the date of the update. :-/
TABLE "USER"
id | user | type_id
--------------------
0 | bibi | 1
1 | toto | 1
TABLE "TYPE"
id | type
-----------
0 | admin
1 | normal
TABLE "USER_UPDATE"
id | user_id | type_id | date
-----------------------------------
0 | 1 | 0 | 9/3/2015
Would like to get something like this:
user | type | date
--------------------
bibi | normal |
toto | admin | 9/3/2015
Hope you guys can help!
I don't know Access, but here's an ANSI SQL answer:
select coalesce(uu.user, u.user), t.type, uu.date
from user u
left join user_update uu on u.id = uu.id
join type t on t.id = coalesce(uu.type_id, u.type_id)
The LEFT JOIN is there to read from user_update if a matching row is found. COALESCE returns the first non-null value, so if a user_update has been found that value is returned, otherwise user value is returned.
NOTE: In ANSI SQL date and user are reserved words, so these may need to be delimited, e.g. "date" or perhaps [date].
Access attempts:
select coalesce(uu.user, u.user), t.type, uu.date
from (user u
left join user_update uu on (u.id = uu.id))
join type t on (t.id = coalesce(uu.type_id, u.type_id))
Or perhaps:
select coalesce(uu.user, u.user), t.type, uu.date
from type t
join (user u
left join user_update uu on (u.id = uu.id))
on (t.id = coalesce(uu.type_id, u.type_id))
As someone new to access, I would recommend you design most of your queries using the "Design View". You want to left join the USER table on both tables, using the USER.type_id = TYPE.id and USER.user = USER_UPDATE.user
The following SQL should accomplish what you want, returning the "USER_UPDATE" record should it have a value, otherwise it will return the "TYPE" record.
You will need to look to see if there can be multiple records in "USER_UPDATE" for the same user, depending on your use, the return of multiple records may not be what you are looking for, you will need to build a select query to return only the most recent record in "USER_UPDATE"
SELECT USER.id, USER.user, USER.type_id, IIf([USER_UPDATE].[type] Is Null, [type].[type],[USER_UPDATE].[type]) AS user_type, USER_UPDATE.date
FROM ([USER] LEFT JOIN TYPE ON USER.type_id = TYPE.id) LEFT JOIN USER_UPDATE ON USER.user = USER_UPDATE.user;