SQL select from two tables, using a distinct value between the two IF it exists, otherwise only using value from the first table? - sql

I have two tables, with structures like this:
roomlist:
-room (unique)
-totalDesks
userlist:
-room (has duplicates)
-deskOwned
I need a SQL statement that will spit out the following:
room
totalDesks
desksUsed (COUNT(DISTINCT userlist.desk))
desksOpen (totalDesks-(COUNT(DISTINCT userlist.desk)))
I have this so far:
SELECT
DISTINCT roomList.room, userlist.room, roomList.totalDesks,
COUNT(DISTINCT userlist.desk) AS desksUsed,
roomlist.totalDesks - COUNT(DISTINCT userlist.desk) AS desksOpen
FROM
roomlist, userlist
WHERE
roomlist.room = userlist.room
The problem is that not all rooms currently have users in them. If I have rooms A, B, C and D, but only records of people in A, B and C, it won't include room D in the result - even though room D has 5 totalDesks, none of which are taken.
How can I get a result that will still give me results on a room from roomlist, even if no records exist for that room in userlist?

Try a LEFT JOIN instead. I assume you need to group by room as well:
SELECT DISTINCT
roomList.room,
userlist.room,
roomList.totalDesks,
COUNT(DISTINCT userlist.desk) AS desksUsed,
roomlist.totalDesks-COUNT(DISTINCT userlist.desk) AS desksOpen
FROM roomlist
LEFT JOIN userlist ON roomlist.room=userlist.room
GROUP BY roomList.room,
userlist.room,
roomList.totalDesks

I would suggest mocking this up in http://sqlfiddle.com/ it will be much easier to help you then

Related

SQL - Returning fields based on where clause then joining same table to return max value?

I have a table named Ticket Numbers, which (for this example) contain the columns:
Ticket_Number
Assigned_Group
Assigned_Group_Sequence_No
Reported_Date
Each ticket number could contain 4 rows, depending on how many times the ticket changed assigned groups. Some of these rows could contain an assigned group of "Desktop Support," but some may not. Here is an example:
Example of raw data
What I am trying to accomplish is to get the an output that contains any ticket numbers that contain 'Desktop Support', but also the assigned group of the max sequence number. Here is what I am trying to accomplish with SQL:
Queried Data
I'm trying to use SQL with the following query but have no clue what I'm doing wrong:
select ih.incident_number,ih.assigned_group, incident_history2.maxseq, incident_history2.assigned_group
from incident_history_public as ih
left join
(
select max(assigned_group_seq_no) maxseq, incident_number, assigned_group
from incident_history_public
group by incident_number, assigned_group
) incident_history2
on ih.incident_number = incident_history2.incident_number
and ih.assigned_group_seq_no = incident_history2.maxseq
where ih.ASSIGNED_GROUP LIKE '%DS%'
Does anyone know what I am doing wrong?
You might want to create a proper alias for incident_history. e.g.
from incident_history as incident_history1
and
on incident_history1.ticket_number = incident_history2.ticket_number
and incident_history1.assigned_group_seq_no = incident_history2.maxseq
In my humble opinion a first error could be that I don't see any column named "incident_history2.assigned_group".
I would try to use common table expression, to get only ticket number that contains "Desktop_support":
WITH desktop as (
SELECT distinct Ticket_Number
FROM incident_history
WHERE Assigned_Group = "Desktop Support"
),
Than an Inner Join of the result with your inner table to get ticket number and maxSeq, so in a second moment you can get also the "MAXGroup":
WITH tmp AS (
SELECT i2.Ticket_Number, i2.maxseq
FROM desktop D inner join
(SELECT Ticket_number, max(assigned_group_seq_no) as maxseq
FROM incident_history
GROUP BY ticket_number) as i2
ON D.Ticket_Number = i2.Ticket_Number
)
SELECT i.Ticket_Number, i.Assigned_Group as MAX_Group, T.maxseq, i.Reported_Date
FROM tmp T inner join incident_history i
ON T.Ticket_Number = i.Ticket_Number and i.assigned_group_seq_no = T.maxseq
I think there are several different method to resolve this question, but I really hope it's helpful for you!
For more information about Common Table Expression: https://www.essentialsql.com/introduction-common-table-expressions-ctes/

how to get count of a column along with column from another table

is there any other way for this problem .I wanted to use group by cant use ii.
my solution:
SQL> select (select count(dr_id)
from cus_detail c
where c.dr_id=d.dr_id) as count_cus,dr_name from driver_detail d;
COUNT_CUS DR_NAME
---------- ---------------
1 raju
0 mandi
2 sajuman
3 babu ram coi
0 daju
0 bare babu
use this one.
select count(c.dr_id) as count_cus, d.dr_name
from driver_detail d
inner join cus_detail c on c.dr_id=d.dr_id
group by d.dr_name
I'm not totally sure I understand what you're asking but I think you're asking how to group things and bring in data from another table
I typically prefer to do this as a grouping subquery:
select
d.dr_name,
c.ctr
from
(select dr_id, count(dr_id) ctr from cus_detail group by dr_id) c
INNER JOIN driver_detail d ON c.dr_id=d.dr_id
The reason why here is that by grouping on the id when counting means we can keep a count for two different people (i.e. different id) both called John. If instead we joined on id then grouped on the Name then two different people both called John would have their counts added together
It's also good to group and count in a subquery because sometimes grouping and counting in the outer query introduces wrong counts if three or more tables are related in 1:Many fashion. If a record in a had 3 records in b and 4 records in c, and we join all before we count we will count 12 records. If we group first we will be joining in 1:1 relationship instead and the counter won't inflate erroneously
If two DR_NAME are same then GROUP BY can give different result then what your current query will give.
So better to use an analytical function as the following:
SELECT
COUNT(C.DR_ID) OVER(
PARTITION BY C.DR_ID
) AS COUNT_CUS,
DR_NAME
FROM
DRIVER_DETAIL D
JOIN CUS_DETAIL C ON ( C.DR_ID = D.DR_ID )
Cheers!!
I think you want a basic aggregation query. Your method is correct (although it could be formatted better). The more standard method is join/group by:
select d.dr_name, count(c.dr_id)
from driver_detail d left join
cus_detail c
on c.dr_id = d.dr_id
group by d.dr_name;

How can I access a selected column from my first select-statement in my third-level subslect?

I have a table "Bed" and a table "Component". Between those two I have a m:n relation and the table "BedComponent", where I store the Bed-ID and the Component-ID.
Every Component has a price. And now I want to write a select-statement that gives me the sum of prices for a certain bed.
This is what I have:
SELECT Bed.idBed, Bed.name, SUM(src.price) AS summe, Bed.idCustomer
FROM Bed,
(SELECT price
FROM dbo.Component AS C
WHERE (C.idComponent IN
(SELECT idComponent
FROM dbo.BedComponent AS BC
WHERE 1 = BC.idBed))) AS src
GROUP BY dbo.Bed.idBed, dbo.Bed.name, dbo.Bed.idCustomer;
This statement works. But of course I don't want to write the bed-ID hard coded into my select as it will always calculate the price for bed 1. Instead of the "1" i want to have the current bed-id.
I work with MS SQL Server
Thanks for your help.
I think you want:
select b.idBed, b.name, SUM(src.price) AS summe, b.idCustomer
from bed b join
bedcomponent bc
on b.idBed = bc.idBed join
component c
on c.idComponent = bc.idComponent
group by b.idBed, b.name, b.idCustomer;
The idCustomer looks strange to me in the select and group by, but I don't know what you are trying to achieve.
Also note the use of table aliases, which make the query easier to write and to read.

How to solve this query

i have to two tables in sql:
tbl_Rooms(Room_Id, Room_Beds);
tbl_AllocatedRooms(Room_Id, Bed_No);
i want to get the result as:
1: List of all the rooms in which no bed is allocated.
2: List of rooms in which one or more beds are allocated also the number of remaining beds(for example if i have a room_id = 2 in tbl_Rooms with Beds = 5 and 2 of these beds are allocated i want to get the remaining number of beds)
i have done the first one but cant figure how to get the second one.
Here is my query for the first one:
SELECT * INTO #tempUnAllocated FROM tbl_Rooms WHERE Room_Id NOT IN
(SELECT Room_Id FROM tbl_Allocation);
SELECT A.room_id,A.room_beds,count(B.bed_no) as allocated_beds,
(A.room_beds - count(B.bed_no)) as remaining_beds
from tbl_rooms A LEFT JOIN tbl_AllocatedRooms B
ON A.room_id = B.room_id
group by A.room_id,A.room_beds
having allocated_beds < room_beds
You can refer the solution here.
This should do it, although a solution using a CTE might be more readable, if your RDBMS supports them.
SELECT a.Room_Id, (SELECT Room_Beds FROM tbl_Rooms r WHERE r.Room_Id=a.Room_Id)-COUNT(*) AS unallocatedBeds
FROM tbl_allocatedRooms a
GROUP BY a.Room_Id

SQL - Counting only one table's entries in joint query

This seems like a regular thing to do, but I can't seem to find how to do it.
I have a join query
SELECT a.nom_batim, COUNT(b.maxten) AS NumFaulty
FROM tblTrials AS b, tblRooms AS a
WHERE b.batiment = a.batiment
AND b.maxten > 10
GROUP BY a.nom_batim
ORDER BY a.nom_batim
that should only return a count of the tblTrials entries. However, since I don't know how to code that, it's currently counting all occurances of b.maxten > 10, TIMES all occurances of b.batiment = a.batiment. I have 1 actual occurance of b.maxten > 10 in the table, but 231 occurances of b.batiment = a.batiment (the tables are set up badly, not my choice; these tables are considered read-only to me), so it returns a count of 231.
How do I COUNT(b.maxten) correctly, but still display a.nom_batim as a user-friendly representation of the batiment ID field? (a.nom_batim is the long name for the building #batiment)
UPDATE
This is what I ended up doing so far..
SELECT a.nom_batim, COUNT(b.batiment) AS NumFaulty
FROM (SELECT DISTINCT nom_batim, batiment FROM tblRooms) AS a
INNER JOIN tblTrials AS b ON a.batiment = b.batiment
WHERE b.maxten > 10
GROUP BY a.nom_batim
ORDER BY a.nom_batim
It works but seems like a resource hog when I only need max ~30 values from tblRooms, but have to query all 5000+ rows selecting only distinct batiment values. Is there no way to do this without having a batiment table tblBatiment: batiment, nom_batim I know it's the best way but I don't have the access.
You can perform the count in a sub-query so it only applies to the one table's records:
SELECT ..
FROM (SELECT batiment, COUNT(maxten) FROM tblTrials WHERE maxten > 10) AS b
,tblRooms AS a
...
Otherwise, the count is applying across all records in the final result, because the query engine doesn't differentiate between records coming from one place or another in a COUNT.
Going back to your original query, you can get what you want if you have an identity column on the tblTrials table:
SELECT a.nom_batim, COUNT(distinct b.id) AS NumFaulty
FROM tblTrials b INNER JOIN tblRooms a
ON b.batiment = a.batiment
WHERE b.maxten > 10
GROUP BY a.nom_batim
ORDER BY a.nom_batim
I also replaced your join syntax with the correct join syntax (using the "join" keyword).
Try this:
SELECT a.nom_batim, COUNT(b.maxten) AS NumFaulty
FROM tblTrials AS b, tblRooms AS a
WHERE b.batiment = a.batiment
GROUP BY a.nom_batim
HAVING count(b.maxten) > 10
ORDER BY a.nom_batim
The best I could do so far which works:
SELECT a.nom_batim AS Building, Count(q.batiment) AS Fixes
FROM (SELECT DISTINCT nom_batim, batiment FROM tblRooms) AS a
INNER JOIN tblTrials AS q
ON a.batiment = q.batiment
WHERE q.maxten > 10
GROUP BY a.nom_batim
Seems like the SELECT DISTINCT nom_batim, batiment FROM tblRooms would be slow considering tblTrials may contain 60k entries and tblRooms may contain 10k entries.. but the entries aren't input yet so I can't really test it. Gordon made a good point that if it's returning the same stuff, it's likely ~the same speed. I do have multi-field primary keys so it might help things along as well (not as much as an ID field perhaps but what can you do).
Thanks to others that answered.
Try to use:
HAVING b.maxten>10