To join four table in mssql - sql

MaingroupTable
MubGroupCodeid MainName maincode
1 Health 098
2 Social 078
SubGroup Table
SubGroupCodeid SubName subcode
1 Nursing 211
2 Civics 224
SubandMainGroup table
subandmainid **MubGroupCodeid** **subgroupcodeid**
1 1 1
2 2 2
Student Table
studid studname **subandmainid** (foriegn key of **subandmain group** table)
1 Alex 1
2 siraj 2
then I want to join and concatinate studname-maingroupcode-subgroupcode to get output like below
Alex-098-211
siraj-078-224

This will get you started and explain the joins. You'll probably also want to do some casting for the maincode and subcode, but since it's not 100% clear they aren't already varchar values I left that out.
SELECT s.studname + '-' + m.maincode + '-' + s.subcode
FROM Student s
INNER JOIN SubandMainGroup smg on smg.subandmainid = s.subandmainid
INNER JOIN MainGroup m on m.mubgroupcodeid = smg.mubgroupcodeid
INNER JOIN SubGroup s on s.subgroupcodeid = smg.subgroupcodeid

use join and concat all the required column by using ||
select s.studname ||'-'||subG.subcode ||'-' M.maincode
from
Student s join SubandMainGroup subM on s.subandmainid=subM.subandmainid
join SubGroup subG on subG.SubGroupCodeid=subM.subgroupcodeid
join MaingroupTable M on M.MubGroupCodeid=subM.MubGroupCodeid

Use the below query to solve the problem.
select stu.studname + '-'+mgrp.maincode +'-'+sgrp.subcode from Student_ stu
join Maingroup mgrp on stu.studid=mgrp.MubGroupCodeid
join SubGroup sgrp on sgrp.SubGroupCodeid=stu.studid

Related

Including rows with no values in SQL query output

Running the following query in my SQL SERVER 2016 database:
SELECT
u.idnumber,
gi.idnumber AS code,
gg.finalgrade AS grade
FROM
grades AS gg
INNER JOIN grade_items AS gi ON gg.itemid = gi.id
INNER JOIN users AS u ON gg.userid = u.id
WHERE gi.idnumber IN ('436','434','313','002','135')
AND (u.idnumber = 'JohnBrown-xyz123);
gives me this result:
-------------------------
idnumber code grade
-------------------------
John12558 313 96
John12558 135 83
How can I include a row for ALL the 5 gi.idnumber's, including the rows with no grade?
-------------------------
idnumber code grade
-------------------------
John12558 436
John12558 434
John12558 313 96
John12558 002
John12558 135 83
Database tables:
user
id PK
grade_items
id PK
grade_grades
grade_grades.itemid FK (grade_items)
grade_grades.userid FK (user)
You can use value construct like that :
select tt.idnumber,
t.idnumber AS code,
tt.grade
from (values ('436'), ('434'), ('313'), ('002'), ('135')) t (idnumber)
left join (select
u.idnumber,
gi.idnumber AS code,
gg.finalgrade AS grade
from grades AS gg
inner join grade_items AS gi ON gg.itemid = gi.id
inner join users AS u ON gg.userid = u.id
where u.idnumber = 'JohnBrown-xyz123') tt on left(tt.code, 3) = t.idnumber
If, idnumber has numeric type, then you don't need to use ' '.
You need cross join to see all you want:
select
u.idnumber,
gi.idnumber code,
ISNULL(gg.finalgrade, 0) grade
from users u
cross join grade_items gi
left outer join grades gg on gg.itemid = gi.id and gg.userid = u.id
where gi.idnumber in ('436','434','313','002','135')
and u.idnumber = 'JohnBrown-xyz123'
nulls in finalgrade cannot be replaced with 'N/A' because they are numbers, not strings, I replaced them with zero.
Assuming "no value" is NULL then just add an extra clause to your WHERE:
WHERE (LEFT(gi.idnumber, 3) IN ('436','434','313','002','135')
OR gi.idnumber IS NULL)
Edit: Apaprently, the above does not work, which implies that "no value" does not mean NULL. you could, instead, therefore try:
WHERE (LEFT(gi.idnumber, 3) IN ('436','434','313','002','135','')
If this doesn't work, please provide consumable sample data, which we can test against: Forum Etiquette: How to post data for a T-SQL Question

SELF-JOIN discarding true CROSS JOIN rows

I have the following query;
What I get is tickets information. I use self-join to obtain the requester and the assignee in the same row:
SELECT z.id AS TICKET, z.name AS Subject, reqs.name AS Requester, techs.name AS Assignee,
e.name AS Entity,DATE_FORMAT(tt.date,'%y%-%m%-%d') AS DATE,
DATE_FORMAT(tt.date,'%T') AS HOUR,
CASE WHEN z.priority = 6 THEN 'Mayor' WHEN z.priority = 5 THEN 'Muy urgente' WHEN z.priority = 4 THEN 'Urgente'WHEN z.priority = 3 THEN 'Mediana' WHEN z.priority = 2 THEN 'Baja' WHEN z.priority =1 THEN 'Muy baja' END AS Priority,
c.name AS Category, i.name AS Department
FROM glpi_tickets_users tureq
JOIN glpi_tickets_users tutech ON tureq.tickets_id = tutech.tickets_id
JOIN glpi_users AS reqs ON tureq.users_id = reqs.id
JOIN glpi_users AS techs ON tutech.users_id = techs.id
JOIN glpi_tickets z ON z.id = tureq.tickets_id
LEFT OUTER JOIN glpi_tickettasks tt ON z.id = tt.tickets_id
LEFT JOIN glpi_itilcategories i ON z.itilcategories_id = i.id
LEFT JOIN glpi_usercategories c ON c.id = reqs.usercategories_id
INNER JOIN glpi_entities e ON z.entities_id = e.id
WHERE (tureq.id < tutech.id AND tureq.type < tutech.type) OR
(tureq.id < tutech.id AND tureq.users_id = tutech.users_id) OR
(tureq.id = tutech.id AND tureq.users_id = tutech.users_id)
The problem is that I get something like that:
1 Report jdoe jdoe Development 16-06-07 11:56:17 Mediana Software Mkt
1 Report jdoe fwilson Development 16-06-07 11:56:17 Mediana Software MKt
1 Report fwilson fwilson Development 16-06-07 11:56:17 Mediana Software Mkt
2 Task11 gwilliams gwilliams Ops 16-06-08 12:00:00 ALTA Hardware Def
3 Task12 gwilliams gwilliams Ops 16-06-08 12:01:00 ALTA Hardware Def
I don't want first and third row because is a CROSS JOIN result. Second row is OK, because jdoe is a requester and fwilson an assignee.
The problem is that sometimes requester and assignee are the same, eg: he creates a ticket for a task that himself will do. For example, 4th and 5th rows are OK.
So, how should I do to make a difference for those distinct cases, i.e.: I need to include:
tureq.id = tech.id AND req.users_id = tech.users.id
BUT NOT IF ALREADY EXISTS
tureq.id = tech.id AND req.users_id <> tech.users_id
Update
The main problem is that a user can assign to himself a ticket:
SELECT * from glpi_tickets_users WHERE type = 2 GROUP BY tickets_id HAVING COUNT(users_id)<2 limit 3;
+----+------------+----------+------+------------------+-------------------+
| id | tickets_id | users_id | type | use_notification | alternative_email |
+----+------------+----------+------+------------------+-------------------+
| 1 | 2 | 12 | 2 | 1 | NULL |
| 3 | 6 | 13 | 2 | 1 | NULL |
| 7 | 8 | 14 | 2 | 1 | NULL |
+----+------------+----------+------+------------------+-------------------+
Update 2:
It was a human mistake. The problem was really not about self-assigned tickets. Rather it was either that some tickets had not Requester or had Requester but still had not any resolver assigned.
I've found
As there are always the two types per ticket you are interested in, you can simply select the according records, so as to get requester and assignee per ticket.
select
t.id as ticket,
t.name as subject,
requester.name as requester,
assignee.name as assignee,
e.name as entity,
date_format(tt.date,'%y%-%m%-%d') as date,
date_format(tt.date,'%T') as hour,
case t.priority
when 6 then 'Mayor'
when 5 then 'Muy urgente'
when 4 then 'Urgente'
when 3 then 'Mediana'
when 2 then 'Baja'
when 1 then 'Muy baja'
end as priority,
uc.name as category,
ic.name as department
from glpi_tickets t
join glpi_entities e on e.id = t.entities_id
join
(
select tu.tickets_id, u.name, u.usercategories_id
from glpi_tickets_users tu
join glpi_users u on u.id = users_id
where tu.type = 1
) requester on requester.tickets_id = t.id
join
(
select tu.tickets_id, u.name
from glpi_tickets_users tu
join glpi_users u on u.id = users_id
where tu.type = 2
) assignee on assignee.tickets_id = t.id
left join glpi_itilcategories ic on ic.id = t.itilcategories_id
left join glpi_usercategories uc on uc.id = requester.usercategories_id;
left outer join glpi_tickettasks tt on tt.tickets_id = t.id
The only thing I wonder is: There can be several ticket tasks per ticket. So what do you want to do then? Have one line per ticket task in your results? This is what the query does. Only, it looks queer that your result rows don't contain any information on the tasks except for the dates, so you may have many, many lines with the same data, only with different dates. So maybe, you'd rather want the first or last date per ticket. To get the last date per ticket, you'd replace the last line in the query with:
left outer join
(
select tickets_id, max(date) as date
from glpi_tickettasks
group by tickets_id
) tt on tt.tickets_id = t.id
And you probably want to add an ORDER BY clause.
you need to add more qualifiers to your joins for example
JOIN glpi_tickets_users tutech ON tureq.tickets_id = tutech.tickets_id and tutech.type = 2

Where clause with multiple values

I am new to SQL and I am using MS SQL Server Management Studio 2014. I have 3 tables called Pizza, Pizza_Topping and Topping. I want to list the pizzas that have TOPPING1 and TOPPING2 as toppings.
This is what I have came up with,
select Pizza.pizzaID, Pizza.pizzaName, Topping.toppingName
from Pizza left join Pizza_Topping on
Pizza.pizzaID = Pizza_Topping.pizzaID
left join Topping on
Topping.toppingID = Pizza_Topping.toppingID
where Topping.toppingName in ('topping1', 'topping2')
and this gives me
pizzaID pizzaName toppingName
-------- ---------------- --------------
PZ002 | PIZZA1 | TOPPING1
PZ002 | PIZZA1 | TOPPING2
PZ010 | PIZZA5 | TOPPING1
PZ010 | PIZZA5 | TOPPING2
PZ011 | PIZZA6 | TOPPING1
PZ012 | PIZZA7 | TOPPING2
I only need first four rows because last two pizzas only have one of the toppings not both.
I have tried this as well,
GROUP BY Pizza.pizzaID,Pizza.pizzaName, Topping.toppingName HAVING COUNT(toppingName) >= 2
but it didn't give the expected result. that line can be used if I am going to display only pizzaID and pizzaName but i want to display toppingName as well.
to be more clear, this what I am expecting
pizzaID pizzaName toppingName
-------- ---------------- --------------
PZ002 | PIZZA1 | TOPPING1
PZ002 | PIZZA1 | TOPPING2
PZ010 | PIZZA5 | TOPPING1
PZ010 | PIZZA5 | TOPPING2
Please tell me how to achieve this result. Thanks
You are almost correct. You need to remove ToppingName from the GROUP BY:
GROUP BY Pizza.pizzaID, Pizza.pizzaName
HAVING COUNT(toppingName) >= 2
You also need to remove it from the SELECT if it is also there:
select p.pizzaID, p.pizzaName
from Pizza p join
Pizza_Topping pt
on p.pizzaID = pt.pizzaID join
Topping t
ont.toppingID = pt.toppingID
where t.toppingName in ('topping1', 'topping2')
group by p.pizzaID, p.pizzaName
having count(*) >= 2;
Also notice two things. Because you have a condition on toppings, the left join is unnecessary. You are only looking for matches, so inner join is appropriate. The use of table aliases makes the query easier to write and to read.
This assumes that pizzas cannot have duplicate toppings. If so, then change the last condition to:
having count(distinct t.toppingName) >= 2
I think this could be done more easily if you can get the count() in your subquery and then join back all the pizzaid's that have count greater than 1 something like this:
SELECT q1.pizzaID
,q1.PizzaName
,t.ToppingName
FROM (
SELECT Pizza.pizzaID
,Pizza.pizzaName
,count(topping.ToppingName) total_count
FROM Pizza
INNER JOIN Pizza_Topping ON Pizza.pizzaID = Pizza_Topping.pizzaID
INNER JOIN Topping ON Topping.toppingID = Pizza_Topping.toppingID
GROUP BY Pizza.pizzaID
,Pizza.pizzaName
HAVING count(topping.ToppingName) > 1
) q1
INNER JOIN Pizza_Topping pt ON q1.pizzaID = pt.pizzaID
INNER JOIN Topping t ON t.toppingID = pt.toppingID
WHERE t.toppingName in ('topping1', 'topping2')
Get your result without topping name . Wrap it in a view and do an inner join with Topping table .
To include the toppings you can do the COUNT-logic using Windowed Aggregate Functions:
SELECT p.pizzaID, p.pizzaName, dt.toppingName
FROM Pizza AS p
JOIN
(
SELECT p.pizzaID, t.toppingName,
COUNT(*) OVER (PARTITION BY p.pizzaID) AS cnt -- number of matching toppings
FROM Pizza_Topping AS pt
JOIN Topping AS t
ON t.toppingID = pt.toppingID
WHERE t.toppingName IN ('topping1', 'topping2')
) AS dt
ON p.pizzaID = dt.pizzaID
WHERE cnt = 2 -- number of searched toppings
You have to join to the pizza_topping and topping tables twice.
select yourfields
from pizza join pizza_topping pt1 on pizza.pizzaID = pt1.pizzaID
join topping t1 on pt1.toppingID = t1.toppingId
join pizza_topping pt2 on pizza.pizzaID = pt2.pizzaID
join topping t2 on pt2.toppingID = t2.toppingId
where t1.toppingName = 'topping1'
and t2.toppingName = 'topping2'
You can solve this problem by using group by statement restricted to Pizza Id. Please refer below query.
select Pizza.pizzaID, Pizza.pizzaName, Pizza_Toppings.Remark
from Pizza join Pizza_Toppings on
Pizza.pizzaID = Pizza_Toppings.pizzaID
join Toppings on
Toppings.toppingID = Pizza_Toppings.toppingID
where Pizza.PizzaId in (
select Pizza.PizzaID
from Pizza join Pizza_Toppings on
Pizza.pizzaID = Pizza_Toppings.pizzaID
join Toppings on
Toppings.toppingID = Pizza_Toppings.toppingID
where Toppings.TopingName in ('topping1', 'topping2')
group by pizza.PizzaId
having count(Pizza.PizzaId ) > 1)
Please let me know if this does not work for you.

Adding a new column in this SQL Query?

I am querying data from the WIP and Employee tables:
WIP
Id,Name
Employee
Id,Name,Orgnization
Joining both I can query:
select w.ID,e.Organization,w.ConsultantName,e.OrganizationID, w.ConsultantID
from vwWIPRecords w
inner join vwEmployees e on w.ConsultantID=e.ID;
Resutls:
1 VHAA Web User 1 1
2 VHAA NZ RP 1 3
3 VHAA Ghom Mure 1 2
4 VHAA Ghom Mure 1 2
Requirment:
In query add anther column which will concatenate and group by e.Organization and e.ConsultantName but it will be only for first unique record. For next (where name and organization is same) it will not show anything. This column will show unique Consultants of a company. Please see record number 3 and 4 in second example.
1 VHAAWeb User 1 1
2 VHAANZ RP 1 3
3 VHAAGhom Mure 1 2
4 1 2
Thanks a lot for your help
Here is a start. The final column is a flag indicating the row should be blank. Let me know if this works for you so far and I can help further.
select w.ID,e.Organization, w.ConsultantName,
e.OrganizationID, w.ConsultantID, CASE WHEN D.Dup > 1 AND D.ID <> w.ID THEN 'Y'
ELSE 'N' END As HideMe
from vwWIPRecords w
inner join vwEmployees e on w.ConsultantID=e.ID
inner join
(
select MIN(w.ID) As ID, e.Organization,w.ConsultantName,
e.OrganizationID, w.ConsultantID, COUNT(*) AS Dup
from vwWIPRecords w
inner join vwEmployees e on w.ConsultantID=e.ID
) D
ON D.Organization = w.Organization
AND D.ConsultantName = w.ConsultantName
AND D.OrganizationID = w.OrganizationID
AND D.ConsultantID = w.ConsultantID

Count Unique Results in T-SQL

My query is:
SELECT DISTINCT IncidentStatus.IncidentStatusName, Incident.IncidentID AS Bob
FROM Incident
INNER JOIN IncidentMember
ON Incident.IncidentID = IncidentMember.IncidentId
INNER JOIN IncidentStatus
ON Incident.IncidentStatusID = IncidentStatus.IncidentStatusID
WHERE (IncidentMember.MemberId = 6)
And the result is:
IncidentStatusName Bob
---------------------------
Closed 9267
In Progress 9251
In Progress 9289
New 7893
Resolved 7750
Required Result is:
IncidentStatusName Bob
---------------------------
Closed 1
In Progress 2
New 1
Resolved 1
Help Requested.
SELECT IncidentStatus.IncidentStatusName, COUNT(Incident.IncidentID) AS Bob
FROM Incident
INNER JOIN IncidentMember ON Incident.IncidentID = IncidentMember.IncidentId
INNER JOIN IncidentStatus ON Incident.IncidentStatusID = IncidentStatus.IncidentStatusID
WHERE (IncidentMember.MemberId = 6)
GROUP BY IncidentStatus.IncidentStatusName
Or maybe you need COUNT(DISTINCT Incident.IncidentID) (depends on your table structure)
take your PK field, Incident.IncidentID, out of the select distinct clause