SQL Server inner join query from many to many relationship tables - sql

I am new to SQL programming and I am having the following problem.
I have 3 tables :
tblMaschine (id PK, type as char(5))
tblUse (id PK, typeOfUse varchar(255))
tblmaschineUse (mid, uid both PK and each as FK to the above tables)
This is a many to many relationship. I have inserted this data in the tables:
tblmaschine
id|type
--+-----
1 |M1
2 |M2
3 |M3
tbluse
id|typeOfUse
--+---------
1 |U1
2 |U2
and
tblmaschineUse
id|type
--+-----
1 |1
1 |2
2 |1
3 |2
I want to query to find the maschine type from tblmaschine which has the both types of use in table maschineUse.
I am using this query but it returns nothing:
select m.type
from tblmaschine as m
inner join tblmaschineUse as mu on m.id = mu.mid
inner join use as u on u.id = mu.uid
where u.typeOfUse = 'U1' and u.typeOfUse = 'U2';
What am I doing wrong?

An option uses aggregation:
select m.id, m.type
from tblmaschine m
inner join tblmaschineUse mu on mu.id = m.id
where mu.type in (1, 2)
group by m.id, m.type
having count(*) = 2
This assumes no duplicates (id, type) in tblmaschineUse (otherwise, you need having count(distinct type) = 2.
If you want to filter on the name of the type, you need another join:
select m.id, m.type
from tblmaschine m
inner join tblmaschineUse mu on mu.id = m.id
inner join tbluse u on u.id = mu.type
where u.typeOfUse in ('U1', 'U2')
having count(*) = 2
You could also use two exists subqueries:
select m.*
from tblmaschine m
where
exists(select 1 from tblmaschineUse mu inner join tbluse u on u.id = mu.type where u.typeOfUse = 'U1' where mu.id = m.id)
and exists (select 1 from tblmaschineUse mu inner join tbluse u on u.id = mu.type where u.typeOfUse = 'U2' where mu.id = m.id)

Related

Postgres - Query that Join two tables

I have two table that need to join them in one table.
1: First Query
SELECT cu.user_name,
li.successdate
FROM logininfo li
JOIN user_mapping um ON um.user_key = li.username
JOIN cwd_user cu ON um.username = cu.user_name
ORDER BY successdate;
Result:
user_name | successdate
---------------------+-------------------------
K_Daniel | 2018-09-02 13:38:22.331
2: Second Query
WITH last_login_date AS
(SELECT user_id
, to_timestamp(CAST(cua.attribute_value AS double precision)/1000) AS last_login
FROM cwd_user_attribute cua
WHERE cua.attribute_name = 'lastAuthenticated'
AND to_timestamp(CAST(cua.attribute_value AS double precision)/1000) < (CURRENT_DATE))
SELECT c.user_name
, g.group_name
FROM cwd_user c
INNER JOIN last_login_date l ON (c.id = l.user_id)
INNER JOIN cwd_membership m ON (c.id = m.child_user_id)
INNER JOIN cwd_group g ON (m.parent_id = g.id)
WHERE g.group_name LIKE '%CEO-%' ;
Result:
user_name | group_name
------------------+----------------------------------------------------
K_Daniel | CEO-Building1
3-Here is the expected result:
user_name | successdate | group_name
---------------------+-------------------------+-----------------------
K_Daniel | 2018-09-02 13:38:22.331 | CEO-Building1
what is the appropriate query to join these table?
Any idea
Thanks,
after serval workaround I found solution.
WITH last_login_date AS
(SELECT user_id
, to_timestamp(CAST(cua.attribute_value AS double precision)/1000) AS last_login
FROM cwd_user_attribute cua
WHERE cua.attribute_name = 'lastAuthenticated'
AND to_timestamp(CAST(cua.attribute_value AS double precision)/1000) < (CURRENT_DATE))
SELECT c.user_name
, li.successdate
, g.group_name
FROM cwd_user c
INNER JOIN last_login_date l ON (c.id = l.user_id)
INNER JOIN cwd_membership m ON (c.id = m.child_user_id)
INNER JOIN cwd_group g ON (m.parent_id = g.id)
INNER JOIN user_mapping um ON (c.user_name = um.username)
INNER JOIN logininfo li ON (um.user_key = li.username)
WHERE g.group_name LIKE 'CEO-%' ;

Return two relationship in one resultset

Below are the oversimplified inspired from my production schema.
Thus, I want to return all these information in one result set.
My desired result set
I have tried this query but it return wrong result.
SELECT u.*
,f.*
,uv.*
,v.*
FROM User u
LEFT JOIN UserFarm uf ON uf.UserID = u.ID
LEFT JOIN Farm f ON f.ID = uf.FarmID
LEFT JOIN FarmVehicle fv ON f.ID = fv.FarmID
LEFT JOIN UserVehicle uv ON u.ID = uv.UserID
LEFT JOIN Vehicle v ON fv.VehicleID = v.ID
WHERE u.ID = 1
Edit: This is the result from above query.
Could anyone advise me on this, I really need to return in the desired result.
You could build a query to return the data you want in the first row, and null in the columns you don't want for that row. Then create another query for the second row and use union all to combine the data together:
declare #UserVehicle table (UserID int, VehicleID int, IsService bit)
declare #User table (ID int, Name varchar(10))
declare #UserFarm table (UserID int, FarmID int)
declare #Farm table (ID int, Name varchar(10))
declare #FarmVehicle table (FarmID int, VehicleID int)
declare #Vehicle table (ID int, [Type] varchar(10), Name varchar(10))
insert into #UserVehicle values (1, 2, 1)
insert into #User values (1, 'Sam')
insert into #UserFarm values (1, 1)
insert into #Farm values (1, 'Flora')
insert into #FarmVehicle values (1, 1)
insert into #Vehicle values (1, 'Larry', 'Scania'), (2, 'Unknown', 'Civic')
select [User.ID] = u.ID
,[User.Name] = u.Name
,[Farm.ID] = f.ID
,[Farm.Name] = f.Name
,[UserVehicle.UserID] = null
,[UserVehicle.VehicleID] = null
,[UserVehicle.IsService] = null
,[Vehicle.ID] = v.ID
,[Vehicle.Name] = v.Name
from #User u
left join #UserFarm uf on u.ID = uf.UserID
left join #Farm f on uf.FarmID = f.ID
left join #FarmVehicle fv on f.ID = fv.VehicleID
left join #Vehicle v on fv.VehicleID = v.ID
union all
select [User.ID] = u.ID
,[User.Name] = u.Name
,[Farm.ID] = null
,[Farm.Name] = null
,[UserVehicle.UserID] = uv.UserID
,[UserVehicle.VehicleID] = uv.VehicleID
,[UserVehicle.IsService] = case when uv.IsService = 0 then 'No' else 'Yes' end
,[Vehicle.ID] = v.ID
,[Vehicle.Name] = v.Name
from #User u
left join #UserFarm uf on u.ID = uf.UserID
left join #UserVehicle uv on u.ID = uv.UserID
left join #Vehicle v on uv.VehicleID = v.ID
This query returns the following dataset:
User.ID User.Name Farm.ID Farm.Name UserVehicle.UserID UserVehicle.VehicleID UserVehicle.IsService Vehicle.ID Vehicle.Name
----------- ---------- ----------- ---------- ------------------ --------------------- --------------------- ----------- ------------
1 Sam 1 Flora NULL NULL NULL 1 Scania
1 Sam NULL NULL 1 2 Yes 2 Civic
If you do not want to see users who are not assigned to vehicles or farms, then change all left join to inner join.
Try with following query
SELECT U.*,T.* FROM (
SELECT F.FARMID,F.NAME AS NAM
,UV.*
,V.*
FROM VEHICLE V
LEFT JOIN FARMVEHICLE FV ON FV.VEHICLEID = V.ID
LEFT JOIN USERVEHICLE UV ON UV.VEHICLEID = V.ID
LEFT JOIN FARM F ON F.FARMID = FV.FARMID
LEFT JOIN USERFARM UF ON UF.FARMID = F.FARMID)T, USER U
Let me know if any issue in query.
You want to show a user's farms and vehicles. However farms and vehicles can be related, and in that case you want to show them together in a row rather than in separated rows.
Example: User1 is related to Farm1, Farm2, and Farm3 and to Vehicle1, Vehicle2, and Vehicle3. Moreover, Farm1 is related to Vehicle1 and Vehicle2 and Farm2 is also related to Vehicle1.
Then you want:
user | farm | vehicle
------+-------+---------
User1 | Farm1 | Vehicle1
User1 | Farm1 | Vehicle2
User1 | Farm2 | Vehicle1
User1 | Farm3 | -
User1 | - | Vehicle3
So the second and third columns show the relations of farms with vehicles. You get these with a full outer join. The join's ON clause is a bit tricky. You want the userid to match and the combination of farmid and vehicleid to be found in the bridge table farmvehicle. Here is one way to do this:
with rel as
(
select coalesce(uf.userid, uv.userid) as userid, uf.farmid, uv.vehicleid
from userfarm uf
full outer join uservehicle uv
on uf.userid = uv.userid
and exists (select * from farmvehicle fv where fv.farmid = uf.farmid
and fv.vehicleid = uv.vehicleid)
)
select u.name as user_name, f.name as farm_name, v.name as vehicle_name
from usr u
left join rel on u.id = rel.userid
left join farm f on f.id = rel.farmid
left join vehicle v on v.id = rel.vehicleid
order by user_name, farm_name, vehicle_name;
Rextester demo: http://rextester.com/DNKSU25931

Select totals to return only one record in SQL Server

After spending more than 3 hours on this I gave up.
I have four tables:
Users, Approvals, Centers, Managements
My ultimate goal is to get the total number of users in each management separated by the user role (I have two roles here : Parents and Society members)
I've been using the following code
select
(select count(r.StudentId)
from Users u
where u.UserId = r.StudentId and u.RoleId = 10) as Parents,
(select count(r.StudentId)
from Users u
where u.UserId = r.StudentId and u.RoleId = 11) as SocietyMembers,
e.ManagementId, e.ManagmentTitle
from
CentersRegistrationsApprovals r --ON r.StudentId = u.UserId
inner join
Centers c ON c.CenterId = r.CenterId
inner join
Managments e ON e.ManagementId = c.EducationManagementId
group by
e.ManagementId, e.ManagmentTitle, StudentId
I'm expecting the query result to be as the following :
Parents SocietyMambers ManagementId ManagementName
----------------------------------------------------------------
3 3 10 North Region
However the result set always gives me
Parents SocietyMambers ManagementId ManagementName
----------------------------------------------------------------
3 NULL 10 North Region
NULL 3 10 North Region
Any ideas how to consolidate the result to only 1 record?
You can query like below:
select
Sum(case when u.roleId = 10 then 1 else 0 end) as Parents,
Sum(case when u.roleId = 11 then 1 else 0 end) as SocietyMembers,
e.ManagementId, e.ManagmentTitle
from
CentersRegistrationsApprovals r --ON r.StudentId = u.UserId
inner join
Centers c ON c.CenterId = r.CenterId
inner join
Managments e ON e.ManagementId = c.EducationManagementId
Join Users u ON r.StudentId = u.UserId
group by
e.ManagementId, e.ManagmentTitle
Please try something like this (not tested)
; with CTE1 as (
select
(select count(r.StudentId)
from Users u
where u.UserId = r.StudentId and u.RoleId = 10) as Parents,
(select count(r.StudentId)
from Users u
where u.UserId = r.StudentId and u.RoleId = 11) as SocietyMembers,
e.ManagementId, e.ManagmentTitle
from
CentersRegistrationsApprovals r --ON r.StudentId = u.UserId
inner join
Centers c ON c.CenterId = r.CenterId
inner join
Managments e ON e.ManagementId = c.EducationManagementId
group by
e.ManagementId, e.ManagmentTitle, StudentId
)
SELECT MAX(Parents), MAX(SocietyMembers), ManagementId, StudentId
FROM CTE1
GROUP BY ManagementId, StudentId

SELECT common entities only based on different corresponding entities

3 Tables
Client -
CID Name
1 Ana
2 Bana
3 Cana
ClientProgram (Bridge Table) -
CID PID
1 4
1 5
1 8
2 10
Program -
PID Program
4 X
5 Y
8 Z
10 G
Desired Output:
Name Program
Ana X
Ana Y
I want to extract only those Clients which are common/exist in different Programs I choose (say X and Y in this case)
Query attempt:
SELECT
C.Name
,P.Program
FROM ClientProgram CP
INNER JOIN Client C
ON CP.CID=C.CID
INNER JOIN Program P
ON CP.PID=P.PID
INNER JOIN ClientProgram CP1
ON CP.CID=CP1.CID
WHERE P.Program = 'X' OR P.Program = 'Y'
AND CP.CID = CP1.CID
This however doesn't pulls in all clients and not only those which exist in multiple programs.
;WITH cte AS (
SELECT
c.Name
,p.Program
,COUNT(*) OVER (PARTITION BY c.CID) as ProgramCount
FROM
Program p
INNER JOIN ClientProgram cp
ON p.PID = cp.PID
INNER JOIN Client c
On cp.CID = c.CID
WHERE
p.Program IN ('X','Y')
)
SELECT Name, Program
FROM
cte
WHERE
ProgramCount > 1
The use of COUNT(*) over will be a problem if PID is not unique in Programs or if the combination of CID to PID in ClientProgram is not unique. However I would assume uniqueness based on what I see.
If not you can go a route like this:
;WITH cte AS (
SELECT
cp.CID
FROM
Program p
INNER JOIN ClientProgram cp
ON p.PID = cp.PID
WHERE
p.Program IN ('X','Y')
GROUP BY
cp.CID
HAVING
COUNT(DISTINCT p.PID) > 1
)
SELECT
c.Name
,p.Program
FROM
cte t
INNER JOIN Client c
ON t.CID = c.CID
INNER JOIN ClientProgram cp
ON t.CID = cp.CID
INNER JOIN Program p
ON cp.PID = p.PID
AND p.Program IN ('X','Y')
This is kind of a round about way of doing it. Probably a better way but this will do it. I through in scripts for temp table in case someone else wants to improve. Could do a temp table for example instead of CTE.
create table #client(cid int,name varchar(20))
create table #clientprogram (cid int, pid int)
create table #program( pid int, program varchar(20))
insert into #client
values(1,'Ana')
,(2,'Bana')
,(3,'Cana')
insert into #clientprogram
values (1,4)
,(1,5)
,(1,8)
,(2,10)
,(2,4)
insert into #program
values (4,'x')
,(5,'y')
,(8,'z')
,(10,'g')
WITH CHECKPLEASE AS(
Select c.Name,ISNULL(p.Program,p2.PRogram) Program
from #client c
inner join #clientprogram cp
on c.CID = cp.CID
left join #program p
on cp.PID = p.PID
and p.PRogram = 'X'
left join #program p2
on cp.PID = p2.PID
and p2.Program = 'Y'
where ISNULL(p.Program,p2.PRogram) is not null
)
Select *
From CHECKPLEASE
where Name in (
SELECT Name
From CHECKPLEASE
group by Name
having COUNT(*) > 1)

sql : query same column multiple times AS different columns, with multiple values returned 1 per row

I need to build a report that is structured as follows:
Name, Field_1, Field_2, Field_3
Jim, opt_1, y, 12
Jane, opt_2, n, 64
etcetera
I'm pulling from Moodle database tables, structure shown in following images. mdl_user_info_data, mdl_user_info_field, mdl_user has same columns as others, all I'm getting from it is name so I'm not bothering to include it here. That part works fine.
Here's the best I could come up with so far, so you can see how the tables join:
SELECT
CONCAT (u.firstname, u.lastname) Name,
(SELECT d.data FROM mdl_user_info_data d JOIN mdl_user_info_field f ON f.id = d.fieldid WHERE f.shortname = "Field_1" GROUP BY d.userid),
(SELECT d.data FROM mdl_user_info_data d JOIN mdl_user_info_field f ON f.id = d.fieldid WHERE f.shortname = "Field_2" GROUP BY d.userid),
(SELECT d.data FROM mdl_user_info_data d JOIN mdl_user_info_field f ON f.id = d.fieldid WHERE f.shortname = "Field_3" GROUP BY d.userid),
FROM mdl_user u
JOIN mdl_user_info_data d ON d.userid = u.id
JOIN mdl_user_info_field f ON d.fieldid = f.id
GROUP BY d.userid
ORDER BY `Name` ASC
I know I'm barking up the wrong tree with my sub-queries, but not sure what to do in place of the GROUP BYs. My current query will return all values of each given Field_x for in each row, when I need to return only the value that corresponds to the user named in column 1.
Any help much appreciated.
I'm not positive about moodle or it's sql syntax, but if common, you might get what you want by doing left-joins to the table multiple times, each with its criteria qualification, then just use the different ALIAS references to get your pieces...
SELECT
CONCAT (u.firstname, u.lastname) Name,
dFld1.Data as Field1Data,
dFld2.Data as Field2Data,
dFld3.Data as Field3Data
FROM
mdl_user u
LEFT JOIN mdl_user_info_data dFld1
ON u.id = dFld1.userid
LEFT JOIN mdl_user_info_field fFld1
ON dFld1.fieldid = fFld1.id
AND fFld1.shortname = "Field_1"
LEFT JOIN mdl_user_info_data dFld2
ON u.id = dFld2.userid
LEFT JOIN mdl_user_info_field fFld2
ON dFld2.fieldid = fFld2.id
AND fFld2.shortname = "Field_2"
LEFT JOIN mdl_user_info_data dFld3
ON u.id = dFld3.userid
LEFT JOIN mdl_user_info_field fFld3
ON dFld3.fieldid = fFld3.id
AND fFld3.shortname = "Field_3"
ORDER BY
`Name` ASC
I have this query with LEFT-JOINs if any such links may NOT have a value.
As for the group by, this would only be required if there are multiple entries for one or more of the underlying tables for a given user and field 1, 2 or 3 entries respectively.
An alternative solution.
SELECT u.id, u.firstname, u.lastname, f.fieldname1, f.fieldname2
FROM mdl_user u
JOIN (
SELECT d.userid,
MAX(CASE WHEN f.shortname = 'fieldname1' THEN d.data ELSE null END) AS fieldname1,
MAX(CASE WHEN f.shortname = 'fieldname2' THEN d.data ELSE null END) AS fieldname2
FROM mdl_user_info_field f
JOIN mdl_user_info_data d ON d.fieldid = f.id
WHERE f.shortname IN ('fieldname1', 'fieldname2')
GROUP BY d.userid) f ON f.userid = u.id
Concat will work for MySql, but you should use database compatible functions - https://docs.moodle.org/dev/Data_manipulation_API#SQL_compatibility_functions
So use the result of
$DB->sql_concat('u.firstname', 'u.lastname');