Select Parent and Child if child has records - sql

I have 3 tables: firearms_firearmbook, inventory_inventory and inventory_inventorytransaction.
And i have 2 Cases:
If inventory_inventory Table has no inventory_inventorytransaction with item action of 16, show inventory_inventory record
If inventory_inventory Table has inventory_inventorytransaction with item action of 16, show inventory_inventory and inventory_inventorytransaction.item_action_id
The query bellow works for the first cause, but its not working for the second cause.
It should show smth like this:
-----------------------------------
| id | serial | action_id |
-----------------------------------
1 MAGUM
2 EAGLE
2 EAGLE 16
Query:
SELECT FB.id, INV.serial_number, INV.id, INVT.item_action_id FROM firearms_firearmbook AS FB
LEFT JOIN inventory_inventory AS INV ON INV.id = FB.item_id
LEFT JOIN inventory_inventorytransaction AS INVT ON INVT.item_id = INV.id AND INVT.item_action_id = 16
WHERE FB.store_id = 1

It seems that if there is an action 16 record for a given item, you need two records in the output: one with a blank in the third column, and one with 16 in the third column.
In that case you could do this:
with base as (
select fb.id,
inv.serial_number,
inv.id,
from firearms_firearmbook as fb
inner join inventory_inventory as inv
on inv.id = fb.item_id
where fb.store_id = 1
)
select base.*,
invt.item_action_id
from base
inner join inventory_inventorytransaction as invt
on invt.item_id = base.id
and invt.item_action_id = 16
union all
select base.*,
null
from base
order by 1, 2, 3, 4 desc

Hi I think this is what your after.
I've made a CASE statement for the two situation you describe. I wasn't sure what you column you wanted for 2) when you say "inventory_inventory" so I put the id column and you can change it for another column.
I've also removed INVT.item_action_id = 16 part of the inventory_inventorytransaction join as I don't think it's necessary as it is handled in the CASE statement.
SELECT
FB.id,
INV.serial_number,
INV.id,
INVT.item_action_id,
CASE
WHEN INVT.item_action_id = 16 THEN CAST(INV.id as VARCHAR) + ' ' + CAST(INVT.item_action_id as VARCHAR) -- This is Case 2
ELSE INV.record -- This is Case 1
END as answer
FROM firearms_firearmbook AS FB
LEFT JOIN inventory_inventory AS INV ON INV.id = FB.item_id
LEFT JOIN inventory_inventorytransaction AS INVT ON INVT.item_id = INV.id -- Removed JOIN on INVT.item_action_id = 16 and moved it to CASE
WHERE FB.store_id = 1;

Related

Nesting Queries to get multiple column results

Have two queries , one collects moves in based on property and unit type the other would collect based on Move Outs for the same data. when ran separately they yield the correct information (move outs are 6 and move ins are 11) Have tried nesting in select and from statements but not getting what i need. When nested within the select am getting the correct move outs per unit type, but each line for move ins is total move ins. I recall that the nesting here would only return one value but know there is a way to return the value for each row. Any assistance is appreciated.
SELECT
p.scode as PropNumber,
p.saddr1 propname,
ut.scode as UnitType,
COUNT(t.hmyperson) as Moveouts,
(
SELECT COUNT(t.hmyperson) as MoveIns
FROM
tenant t
JOIN unit u ON t.hunit = u.hmy
JOIN property p ON p.hmy = u.hproperty
JOIN unittype ut ON ut.hmy = u.HUNITTYPE
WHERE
t.dtmovein >= getdate() - 14
AND p.scode IN ('gsaff')
) mi
FROM
Property p
JOIN unit u ON u.hproperty = p.hmy
JOIN tenant t ON t.hunit = u.hmy
JOIN unittype ut ON ut.hmy = u.HUNITTYPE
WHERE
p.scode IN ('gsaff')
AND t.DTMOVEOUT >= getdate()- 14
GROUP BY
ut.scode,
p.scode,
p.saddr1
With this data is coming out like :
PropNumber Propname UnitType MoveOuts MoveIns
1 x tc2 1 11
1 x tc3 2 11
1 x tc4 1 11
1 x tc5 1 11
1 x tc6 1 11 <pre>
Move in column should display as
2
5
1
0
3
You need to correlate the subquery according to the record being processed in the outer query. This also requires that you use different table aliases in the subquery than in the outer query.
It is hard to tell without seeing sample data, however I would expect that you need to correlate with all non-aggregated columns in the outer query.
Try changing :
(
SELECT COUNT(t.hmyperson) as MoveIns
FROM
tenant t
JOIN unit u ON t.hunit = u.hmy
JOIN property p ON p.hmy = u.hproperty
JOIN unittype ut ON ut.hmy = u.HUNITTYPE
WHERE
t.dtmovein >= getdate() - 14
AND p.scode IN ('gsaff')
) mi
To :
(
SELECT COUNT(t.hmyperson) as MoveIns
FROM
tenant t1
JOIN unit u1 ON t1.hunit = u1.hmy
JOIN property p1 ON p1.hmy = u1.hproperty
JOIN unittype ut1 ON ut1.hmy = u1.HUNITTYPE
WHERE
t1.dtmovein >= getdate() - 14
AND p1.scode IN ('gsaff')
AND p1.scode = p.scode
AND p1.saddr1 = p.saddr1
AND ut1.scode = ut.scode
) mi

How to get fields from multiple tables

I want to get fields from 2 different tables . The last field candidate_score_id has a many to one relationship. So how should I join the below 2 queries
1) To get candidate_score_id from the candidate_score table.
select candidate_score_id from candidate_score a where
a.assessment_id = NEW.assessment_id and
a.candidate_id = NEW.candidate_id and
a.attempt_Count = NEW.attempt_count;
2) To insert different fields in to the candidate_score_details table. The field in this table should be obtained by query above.
insert into candidate_score_details(candidate_score_details_id, candidate_id, assessment_id, attempt_count, score_type, score_tag,correct, candidate_score_id)
select uuid();
select a.candidate_id, a.assessment_id,a.attempt_count,"BY-COMPLEXITY",
case c.complexity
when 1 then "HIGH"
when 2 then "MEDIUM"
when 3 then "LOW"
end, count(*) from candidate_answer a, answer_key b, question_meta_data c where a.candidate_id = NEW.candidate_id and
a.assessment_id = NEW.assessment_id and
a.attempt_count = NEW.attempt_count and
a.assessment_id = b.assessment_id and
a.question_id = b.question_number and
a.response = b.answer and
a.question_id = c.question_number
group by a.candidate_id, a.assessment_id, a.attempt_count, c.complexity;
Just looking at the SQL joining aspect of your question, you'll need to specify the table I THINK you're aliasing a 2nd table with the "NEW" reference. If that's the case, then the query would be (replacing "OTHER_TABLE_NAME" with the name of the 2nd table:
select a.candidate_score_id
from candidate_score a
left join OTHER_TABLE_NAME new on
and a.assessment_id = NEW.assessment_id
and a.candidate_id = NEW.candidate_id
and a.attempt_Count = NEW.attempt_count
Seems that Query 1 has the same 3 criteria on the "candidate_score" table as for the "candidate_answer" table in Query 2.
So how about adding a LEFT JOIN of "candidate_score" to "candidate_answer" on those 3 fields?
For example:
INSERT INTO candidate_score_details
(
candidate_score_details_id,
candidate_id,
assessment_id,
attempt_count,
score_type,
score_tag,
correct,
candidate_score_id
)
SELECT
uuid(),
answer.candidate_id,
answer.assessment_id,
answer.attempt_count,
'BY-COMPLEXITY' AS score_type,
(CASE meta.complexity
WHEN 1 THEN 'HIGH'
WHEN 2 THEN 'MEDIUM'
WHEN 3 THEN 'LOW'
END) AS score_tag,
COUNT(*) AS correct,
MAX(score.candidate_score_id) AS max_candidate_score_id
FROM candidate_answer AS answer
JOIN answer_key AS akey
ON (akey.assessment_id = answer.assessment_id AND akey.question_number = answer.question_id AND akey.answer = answer.response)
LEFT JOIN candidate_score AS score
ON (score.candidate_id = answer.candidate_id AND score.assessment_id = answer.assessment_id AND score.attempt_count = answer.attempt_count)
LEFT JOIN question_meta_data AS meta
ON meta.question_number = answer.question_id
WHERE answer.candidate_id = NEW.candidate_id
AND answer.assessment_id = NEW.assessment_id
AND answer.attempt_count = NEW.attempt_count
GROUP BY answer.candidate_id, answer.assessment_id, answer.attempt_count, meta.complexity;

display reference columns in multi-child SQL

------- id ------- id ------- id ------- id -------
| t-1 | <---> | t-a | <---> | t-b | <---> | s-a | <---> | s-b |
------- ------- ------- ------- -------
SELECT distinct
t-a.col, t-b.col
FROM
INNER JOIN t-1 ON t-1.id = t-a.id
LEFT OUTER JOIN t-b on t-a.id = t-b.id
GROUP BY
T-1.id
So what I want is to display a column from s-b (along with all the other columns being displayed) which is referenced by s-a which is referenced by t-b. I can display anything I want from t-a and t-b but I get errors trying to link up the other sub tables.
SELECT distinct
inspections.inspection_id as InspectionId,
convert(char(3),inspections.Inspection_Type) + ' ' + Inspection_Desc as Inspection,
inspections.current_status as Current_Status,
case when left(inspections.current_status,1) = 'X' then 'SCHEDULED' else inspection_status.status_description end as Current_Status_Name,
convert(varchar(10),max(distinct inspection_history.scheduled_date),101) as 'Last_Scheduled',
convert(varchar(10),max(distinct inspection_history.inspect_date),101) as 'Last_Inspected',
inspections.inspection_type,
case when (inspections.current_status <> 'A' and left(inspections.current_status,1) <> 'X' and left(inspections.current_status,1) <> 'S') then dbo.fn_DependencyCheckMessage(#PermitNum,inspections.inspection_type) else '' end as Dependency_Message,
case when (inspections.current_status <> 'A' and left(inspections.current_status,1) <> 'X' and left(inspections.current_status,1) <> 'S') and (dbo.fn_DependencyCheck(#PermitNum,inspections.inspection_type)=1 and dbo.fn_CheckTempDeposits(#PermitNum,inspections.inspection_type)=1 and dbo.fn_CertOccCheck(#PermitNum,inspections.inspection_type)=1) then 1 else 0 end as AllowSchedule,
inspection_comments.comment_description as comment
FROM inspections
inner join permit_arch on permit_arch.permit_id = Inspections.permit_id
inner join inspection_types on inspection_types.inspection_type = Inspections.inspection_type
LEFT OUTER JOIN inspection_history ON inspections.inspection_id = inspection_history.inspection_id
LEFT OUTER JOIN inspection_status ON inspections.current_status = inspection_status.status_code
LEFT JOIN inspection_history_comment ON inspection_history.insp_hist_id = inspection_history_comment.inspection_history_id
LEFT JOIN inspection_comments ON inspection_history_comment.comment_code = inspection_comments.comment_code
WHERE
permit_arch.permit_number = #PermitNum
AND
(inspection_types.internal_only is null or inspection_types.internal_only = 0)
AND
(inspection_history.status <> 'D' or inspection_history.status is null)
GROUP BY permit_arch.permit_number,permit_arch.permit_status, inspections.Inspection_Type, inspection_types.inspection_desc, inspections.current_status,inspection_status.status_description,
inspections.inspection_id
HAVING Permit_arch.permit_number = #Permitnum and (inspections.current_status <> 'D' or inspections.current_status is null) and inspections.inspection_type <> 34
ORDER BY inspections.inspection_type ASC
My joins are the last two. SSMS errors with:
Msg 8120, Level 16, State 1, Procedure procWebGetPermitInspectionsForSchedulingA, Line 20 [Batch Start Line 7]
Column 'inspection_comments.comment_description' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Msg 145, Level 15, State 1, Procedure procWebGetPermitInspectionsForSchedulingA, Line 10 [Batch Start Line 7]
ORDER BY items must appear in the select list if SELECT DISTINCT is specified.
I want to get the column inspection_comments.description
You don't need group by in your query. It is used when there is an aggregated function like sum, max etc. in select statement. You also missed mentioning t-a before inner join in your query. I have included that in my query.
Also, Left outer join is nothing but Left join itself. Therefore, I changed that also in the query
Try this:-
SELECT distinct
t-a.col, t-b.col, s-b.col
FROM t-a
INNER JOIN t-1 ON t-1.id = t-a.id
LEFT JOIN t-b on t-a.id = t-b.id
LEFT JOIN s-a on t-b.id=s-a.id
LEFT JOIN s-b on s-a.id=s-b.id;

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

Combine results of two different records in one

I have an SQL data stored in a single table but with different type and I want to combine the result in one liner show in the example below. Anyone can give suggestion to this SQL query?
My SQL query to get this results:
SELECT NumValue, Label, Type
FROM (CustomPollerStatusTable
INNER JOIN CustomPollers ON (CustomPollerStatusTable.PollerID =
CustomPollers.CustomPollerID))
INNER JOIN Nodes ON (CustomPollerStatusTable.NodeID = Nodes.NodeID)
WHERE ((Nodes.Caption = 'fqdn') AND
(CustomPollers.Type = 'Student Info') AND
(CustomPollerStatusTable.NumValue > 89) AND
(NOT (CustomPollerStatusTable.Label LIKE '%.snapshot%')) AND
(NOT (CustomPollerStatusTable.Label LIKE '%aggr%')) AND
(NOT (CustomPollerStatusTable.Label = '/vol/scratch/'))
)
====================================
NumValue | Label | Type
====================================
90 | Student 1 | Student Info
10 | Student 1 | Student Class
====================================
The results that I would like to achieve:
==========================================================================
NumValue.Info | NumValue.Class | Label | Type.Info | Type.Class
==========================================================================
90 | 10 | Student 1 | Student Info | Student Class
==========================================================================
This should do it. Slightly different from nrathaus' answer. Obviously you can use as to change the column names to whatever you want.
select
s1.NumValue,
s2.NumValue,
s1.Label,
s1.Type,
s2.Type
from Student s1 inner join Student s2
on s1.Label = s2.Label
where s1.Type like '%Info'
and s2.Type like '%Class'
EDIT: Now that you have posted your select statement I think this might not work. Can you post the table structure?
EDIT2: This might work.
INSERT INTO TempTable(
NumValInfo,
Label,
TypeInfo)
SELECT
c.NumValue,
c.Label,
p.Type
FROM (CustomPollerStatusTable c INNER JOIN CustomPollers p
ON (c.PollerID = p.CustomPollerID))
INNER JOIN Nodes n
ON (c.NodeID = n.NodeID)
WHERE n.Caption = 'fqdn'
AND p.Type = 'Student Info'
AND c.NumValue > 89
AND NOT (c.Label LIKE '%.snapshot%')
AND NOT (c.Label LIKE '%aggr%')
AND NOT (c.Label = '/vol/scratch/')
AND p.Type like '%Info'
UPDATE TempTable set
NumValClass = c.NumValue,
TypeClass = p.Type
FROM (CustomPollerStatusTable c INNER JOIN CustomPollers p
ON (c.PollerID = p.CustomPollerID))
INNER JOIN Nodes n
ON (c.NodeID = n.NodeID)
INNER JOIN TempTable t
ON t.Label = c.Label
WHERE n.Caption = 'fqdn'
AND p.Type = 'Student Info'
AND c.NumValue > 89
AND NOT (c.Label LIKE '%.snapshot%')
AND NOT (c.Label LIKE '%aggr%')
AND NOT (c.Label = '/vol/scratch/')
AND p.Type like '%Class'
SELECT * FROM TempTable
I think this will do:
SELECT tblInfo.NumValue AS `NumValue.Info`,
tblClass.NumValue AS `NumValue.Class`,
Table.Label AS Label,
tblInfo.Type AS `Type.Info`,
tblClass.Type AS `Type.Class`
FROM (Table)
LEFT JOIN Table AS tblInfo ON tblInfo.Label = Table.Label ON tblInfo.Type = 'Student Info'
LEFT JOIN Table AS tblClass ON tblClass.Label = Table.Label ON tblClass.Type = 'Student Class'
GROUP BY Table.Label