Sub query in t sql select statement - sql

I have a query which lists users but it requires a sub query to get whether they are available today. I need the fourth column 'Availability' to iterate through each user and display if they have availability or display a null. I've tried everything I can think of sub queries, cursors etc but no joy. Any pointers welcome!
SELECT inter.authno,
inter.FirstName,
inter.Surname,
COALESCE((
Select at.typeName as [Availability]
FROM [database].[dbo].[Interviewer] inter
full join [database].[dbo].[availability] av
on inter.authno = av.authno
full join [database].[dbo].[availability_days] ad
on av.availID = ad.availID
full join [database].[dbo].[availibiltyType] at
on av.typeID = at.typeid
where exists(
select authno
from [database].[dbo].[Interviewer]
)
and ad.actualDay = '2015-05-21'
), null ) AS [Availability]
FROM [database].[dbo].[Interviewer] inter
The query gives the below results, but it should only show Available for Harry Kane and the rest should be null.
authno FirstName Surname Availability
-------------------------------------
10 Minch Yoda Available
11 Darth Vadar Available
12 Darth Maul Available
14 Obi Wan Kenobi Available
15 Qui-Gon Jinn Available
16 Darth Sidious Available
17 Boba Fett Available
24 Harry Kane Available
39 mark o'neill Available
I also tried the code suggestion below kindly provided which gives some results I need, but it shows all of the results instead of the availability type for today.
SELECT
inter.authno,
inter.FirstName,
inter.Surname,
at.typeName as [Availability]
FROM [database].[dbo].[Interviewer] inter
left JOIN [database].[dbo].[availability] av
on inter.authno = av.authno
left JOIN [database].[dbo].[availability_days] ad
on av.availID = ad.availID
and ad.actualDay = '2015-07-21'
left JOIN [database].[dbo].[availibiltyType] at
on av.typeID = at.typeid
Output:
+----+---------+-----------+--------------+
| 10 | Minch | Yoda | NULL |
+----+---------+-----------+--------------+
| 11 | Darth | Vadar | NULL |
| 12 | Darth | Maul | NULL |
| 13 | Luke | Skywalker | NULL |
| 14 | Obi Wan | Kenobi | NULL |
| 15 | Qui-Gon | Jinn | Annual Leave |
| 16 | Darth | Sidious | NULL |
| 17 | Boba | Fett | UO |
| 17 | Boba | Fett | Available |
| 18 | test22 | test33 | NULL |
| 19 | test7 | test7 | NULL |
| 22 | Bob | Marley | NULL |
| 23 | JO | JO | NULL |
| 24 | Harry | Kane | Annual Leave |
| 24 | Harry | Kane | Available |
| 24 | Harry | Kane | Available |
| 24 | Harry | Kane | Annual Leave |
| 24 | Harry | Kane | Annual Leave |
| 24 | Harry | Kane | NW |
| 24 | Harry | Kane | NW |
| 24 | Harry | Kane | Available |
| 39 | mark | o'neill | US |
+----+---------+-----------+--------------+
I also tried the below which gets me the exact results that I need only that, I need to display all users whether they have a date in the table or not. i.e. If I change the date to last weer Harry Kane disappears.
SELECT
inter.authno,
inter.FirstName,
inter.Surname,
at.typeName as [Availability]
FROM [database].[dbo].[Interviewer] inter
left JOIN [database].[dbo].[availability] av
on inter.authno = av.authno
left JOIN [database].[dbo].[availability_days] ad
on av.availID = ad.availID
left JOIN [database].[dbo].[availibiltyType] at
on av.typeID = at.typeid
where ad.actualDay = '2015-05-21'or ad.actualDay is null
Output for today:
+--------+-----------+-----------+--------------+
| authno | FirstName | Surname | Availability |
+--------+-----------+-----------+--------------+
| 10 | Minch | Yoda | NULL |
| 11 | Darth | Vadar | NULL |
| 12 | Darth | Maul | NULL |
| 13 | Luke | Skywalker | NULL |
| 14 | Obi Wan | Kenobi | NULL |
| 16 | Darth | Sidious | NULL |
| 18 | test22 | test33 | NULL |
| 19 | test7 | test7 | NULL |
| 22 | Bob | Marley | NULL |
| 23 | JO | JO | NULL |
| 24 | Harry | Kane | Available |
+--------+-----------+-----------+--------------+
Output for 2015-05-10
+--------+-----------+-----------+--------------+
| authno | FirstName | Surname | Availability |
+--------+-----------+-----------+--------------+
| 10 | Minch | Yoda | NULL |
| 11 | Darth | Vadar | NULL |
| 12 | Darth | Maul | NULL |
| 13 | Luke | Skywalker | NULL |
| 14 | Obi Wan | Kenobi | NULL |
| 16 | Darth | Sidious | NULL |
| 18 | test22 | test33 | NULL |
| 19 | test7 | test7 | NULL |
| 22 | Bob | Marley | NULL |
| 23 | JO | JO | NULL |
+--------+-----------+-----------+--------------+

If I understand correctly, you want a correlated subquery for your version of the query:
SELECT inter.authno,
inter.FirstName,
inter.Surname,
(Select at.typeName as [Availability]
FROM [database].[dbo].[availability] av join
[database].[dbo].[availability_days] ad
on av.availID = ad.availID join
[database].[dbo].[availibiltyType] at
on av.typeID = at.typeid
where inter.authno = av.authno and ad.actualDay = '2015-05-21'
) AS [Availability]
FROM [database].[dbo].[Interviewer] inter;
Some notes:
COALESCE(<x>, NULL) doesn't make sense. Just use <X>
With a subquery, you should use IFNULL() rather than COALESCE(), because SQL Server has (what I consider to be) a flawed implementation of COALESCE().
Your subquery needs to be correlated to the outer query.
I have no idea what the EXISTS clause was supposed to do. If the table has any rows, then it would always return TRUE.
There is no reason for full joins in the subquery.
I would expect your version to return the error "subquery returns more than one row".

Without seeing your table structure It's a guess, but an educated one.
Try this:
SELECT inter.authno,
inter.FirstName,
inter.Surname,
at.typeName as [Availability]
FROM [database].[dbo].[Interviewer] inter
LEFT JOIN [database].[dbo].[availability] av on inter.authno = av.authno
LEFT JOIN [database].[dbo].[availability_days] ad on av.availID = ad.availID and ad.actualDay = '2015-05-21'
LEFT JOIN [database].[dbo].[availibiltyType] at on av.typeID = at.typeid
For future sql questions you might have, Please include the relevant tables DDL, some sample data (preferably as DML statements), and the desired output.

Related

Get hierarchy of all different level of managers

I'm using pgAdmin4 and I have a SQL table with employee/manager HR data that looks like this:
| employee_id | email_address | full_name | band_lvl | manager_id |
| 5592 | jillr#ex.org | Jill Rhode | 20 | 6521 |
| 6421 | racheln#ex.org | Rachel Nam | 40 | 4251 |
| 2818 | todda#ex.org | Todd Alex | 25 | 6421 |
| 4251 | jalens#ex.org | Jalen Smith | 60 | 2199 |
| 6521 | tolun#ex.org | Tolu Nagoye | 30 | 2199 |
| 7831 | jina#ex.org | Ji Na | 80 | NULL |
| 2199 | zaynm#ex.org | Zayn Mate | 70 | 7831 |
Based on the first manager_id and employee_id, I'm seeking to return the following columns: Level1 Manager Name, Level1 Manager Email, Level1 Manager Band Lvl, Level1 Manager Manager's Id. I then want to do that for each manager that's a step above, until there are no higher managers.
The desired output should look like this:
| employee_id | email_address | full_name | band_lvl | manager_id | Lvl1 Mng Nm | Lvl1 Mng Email | Lvl1 Mng Band Lvl | Lvl1 Mng Mngs Id | Lvl2 Mng Nm | Lvl2 Mng Email | Lvl2 Mng Band Lvl | Lvl2 Mng Mngs Id |
| 5592 | jillr#ex.org | Jill Rhode | 20 | 6521 | Tolu Nagoye | tolun#ex.org | 30 | 2199 | Zayn Mate | zaynm#ex.org | 70 | 7831 |
| 6421 | racheln#ex.org | Rachel Nam | 40 | 4251 | Jalen Smith | jalens#ex.org | 60 | 2199 | Zayn Mate | zaynm#ex.org | 70 | 7831 |
| 2818 | todda#ex.org | Todd Alex | 25 | 6421 | Rachel Nam | racheln#ex.org | 40 | 4251 | Jalen Smith | jalens#ex.org | 60 | 2199 |
| 4251 | jalens#ex.org | Jalen Smith | 60 | 2199 | Zayn Mate | zaynm#ex.org | 70 | 7831 | Ji Na | jina#ex.org | 80 | NULL |
| 6521 | tolun#ex.org | Tolu Nagoye | 30 | 2199 | Zayn Mate | zaynm#ex.org | 70 | 7831 | Ji Na | jina#ex.org | 80 | NULL |
| 7831 | jina#ex.org | Ji Na | 80 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
| 2199 | zaynm#ex.org | Zayn Mate | 70 | 7831 | Ji Na | jina#ex.org | 80 | NULL | NULL | NULL | NULL | NULL |
So far, this is what I've come up with, to get the first columns for the Level 1 Manager; however, I don't know where to go from here, as I'm very new to SQL:
SELECT B.employee_id,
B.email_address,
B.full_name,
B.band_lvl,
B.manager_id,
B1.full_name AS L1_mng_nm,
B1.email_address AS L1_mng_email,
B1.band_lvl AS L1_mng_band_lvl,
B1.manager_id AS L1_mgr_mgrs_id
FROM hrdata B
INNER JOIN hrdata B1 ON
B.manager_id = B1.employee_id;
Your query is close, but you would need to make a few changes to get to your desired output. To begin, I would recommend doing a LEFT JOIN as opposed to an INNER JOIN, as the INNER JOIN will not return null values and will instead drop records that it cannot find a match for in both tables (in this case, if it cannot find a match on manager_id to employee_id from the first use of hrdata to the second use of hrdata).
After that, your query should look similar to what you have already done, just with another self-join to get the second-level manager data:
SELECT B.employee_id,
B.email_address,
B.full_name,
B.band_lvl,
B.manager_id,
B1.full_name AS L1_mng_nm,
B1.email_address AS L1_mng_email,
B1.band_lvl AS L1_mng_band_lvl,
B1.manager_id AS L1_mgr_mgrs_id,
B2.full_name AS L2_mng_nm,
B2.email_address AS L2_mng_email,
B2.band_lvl AS L2_mng_band_lvl,
B2.manager_id AS L2_mgr_mgrs_id,
FROM hrdata B
LEFT JOIN hrdata B1
ON B1.employee_id = B.manager_id
LEFT JOIN hrdata B2
ON B2.employee_id = B1.manager_id

SQL JOIN when empty link still show row

I have 2 tables:
patient:
| patientid | name | address |
| 45 | jelle | adres 2 |
| 23 | piet | adres 6 |
| 11 | kees | adres 4 |
agenda:
| agendaid | patientid | datum | time |
| 1 | 45 | 2020-05-12 | 12:05 |
| 2 | 45 | 2020-07-11 | 16:02 |
| 3 | 11 | 2020-02-10 | 10:35 |
I want to get all patients with their upcoming appointment from agenda
I tried to run this query:
SELECT * FROM patient LEFT JOIN agenda ON patient.patientid = agenda.patientid WHERE agenda.datum >= CURDATE()
But this one returns only patients that have an appointment in the future.
I want to receive all patients, even if they don't have an appointment in the future.
The desired result should be this:
| patientid | name | address | agendaid | patientid | datum | time |
| 45 | jelle | adres 2 | 2 | 45 | 2020-07-11 | 16:02 |
| 23 | piet | adres 6 | | | | |
| 11 | kees | adres 4 | 3 | 11 | 2020-02-10 | 10:35 |
Does anyone know how to achieve this?
I created an SQL fiddle for you guys to check out!
http://sqlfiddle.com/#!9/6eecc4/1
As you can see It returns multiple rows for each patient when he has multiple appointments and does not return patients that have no appointments in the future.
You need to move the filtering condition to the ON clause. Otherwise, you turn the outer join into an inner join:
SELECT p.*, a.*
FROM patient p LEFT JOIN
agenda a
ON p.patientid = a.patientid AND a.datum >= CURDATE();

Transposing Data SQL

The data looks similar to this:
+----+------+-----------+-------+---------+---------+--------+
| ID | Unit | Floorplan | Sq Ft | Name | Amenity | Charge |
+----+------+-----------+-------+---------+---------+--------+
| 1 | 110 | A1 | 750 | Alan | GARAGE | 50 |
| 2 | | | | | RENT | 850 |
| 3 | | | | | PEST | 2 |
| 4 | | | | | TRASH | 15 |
| 5 | | | | | TOTAL | 20 |
| 6 | 111 | A2 | 760 | Bill | STORAGE | 35 |
| 7 | | | | | GARAGE | 50 |
| 8 | | | | | RENT | 850 |
| 9 | | | | | PEST | 2 |
| 10 | | | | | TOTAL | 15 |
| 11 | 112 | A3 | 770 | Charlie | PETRENT | 20 |
| 12 | | | | | STORAGE | 35 |
| 13 | | | | | GARAGE | 50 |
| 14 | | | | | RENT | 850 |
| 15 | | | | | TOTAL | 2 |
+----+------+-----------+-------+---------+---------+--------+
I am new to SQL and trying my best using Microsoft Access, but I need help.
The data needs to look like this:
My first step is to separate the units from the rest with
SELECT * FROM table WHERE Unit <> NULL;
and after that I've usually just hard-input the rest.
My idea was as follows:
INSERT INTO table
VALUES (NULL,NULL,...,'Pest',$2)
FROM table
WHERE NOT EXIST 'Pest' BETWEEN x AND y
/* where x = Total 1 and y = Total 2*/
Am I on the right track? I probably need a loop or a join, but I'm not at that level yet.
You can use a crosstab query, though a bit convoluted it is:
TRANSFORM
Sum(TableUnit.Charge) AS SumOfCharge
SELECT
S.Unit,
S.Floorplan,
S.SqFt,
S.Name,
S.Amenity
FROM
TableUnit,
(SELECT
Q.Id,
Val(DMax("Id","TableUnit","Id<=" & Q.[Id] & " And Unit Is Not Null")) AS ParentId
FROM TableUnit As Q) AS T,
(SELECT
TableUnit.Id,
TableUnit.Unit,
TableUnit.Floorplan,
TableUnit.SqFt,
TableUnit.Name,
TableUnit.Amenity
FROM
TableUnit
WHERE
TableUnit.Unit Is Not Null) AS S
WHERE
TableUnit.Id=[T].[Id]
AND
T.ParentId)=[S].[Id]
GROUP BY
T.ParentId,
S.Unit,
S.Floorplan,
S.SqFt,
S.Name,
S.Amenity
PIVOT
TableUnit.Amenity In
("Garage","Pest","Trash","PetRent","Storage","Rent");
Your test data differs a little from your expected output, so:
My MSAccess is rather rusty, but something like this should work:
SELECT t0.Unit, t0.Floorplan, t0.[Sq Ft], t0.Name, t0.Amenity
, SUM(IIF(tM.Amenity = 'GARAGE', Charge, 0)) AS [Garage]
, SUM(IIF(tM.Amenity = 'PEST', Charge, 0)) AS [Pest]
FROM (
SELECT t1.id AS id0, MIN(t2.id) AS idN
FROM t AS t1
INNER JOIN t AS t2 ON t1.id < t2.id
WHERE t1.Unit <> '' AND t2.Unit <> ''
) AS groups
INNER JOIN t AS t0 ON t0.id = groups.id0
LEFT JOIN t AS tM ON tM.id > groups.id0 AND tm.id < groups.idN
GROUP BY t0.Unit, t0.Floorplan, t0.[Sq Ft], t0.Name, t0.Amenity
;
Though, if I remember correctly, and it hasn't changed in newer versions; you can't have true subqueries and will need to make groups a separate query you can join to as if it were a table/view.

Sum data from two tables with different number of rows

There are 3 Tables (SorMaster, SorDetail, and InvWarehouse):
SorMaster:
+------------+
| SalesOrder |
+------------+
| 100 |
| 101 |
| 102 |
+------------+
SorDetail:
+------------+------------+---------------+
| SalesOrder | MStockCode | MBackOrderQty |
+------------+------------+---------------+
| 100 | PN-1 | 4 |
| 100 | PN-2 | 9 |
| 100 | PN-3 | 1 |
| 100 | PN-4 | 6 |
| 101 | PN-1 | 6 |
| 101 | PN-3 | 2 |
| 102 | PN-2 | 19 |
| 102 | PN-3 | 14 |
| 102 | PN-4 | 6 |
| 102 | PN-5 | 4 |
+------------+------------+---------------+
InvWarehouse:
+------------+-----------+-----------+
| MStockCode | Warehouse | QtyOnHand |
+------------+-----------+-----------+
| PN-1 | A | 1 |
| PN-2 | B | 9 |
| PN-3 | A | 0 |
| PN-4 | B | 1 |
| PN-1 | A | 0 |
| PN-3 | B | 5 |
| PN-2 | A | 9 |
| PN-3 | B | 4 |
| PN-4 | A | 6 |
| PN-5 | B | 0 |
+------------+-----------+-----------+
Desired Results:
+------------+-----------------+--------------+
| MStockCode | SumBackOrderQty | SumQtyOnHand |
+------------+-----------------+--------------+
| PN-1 | 10 | 10 |
| PN-2 | 28 | 1 |
| PN-3 | 17 | 5 |
| PN-4 | 12 | 13 |
| PN-5 | 11 | 6 |
+------------+-----------------+--------------+
I have been going around in circles with no end in sight. Seems like it should be simple but just can't wrap my head around it. The SumBackOrderQty obviously getting counted twice as the SumQtyOnHand is evaluated. To this point I have been doing the calculations in the PHP instead of the select statement but would like to clean things up a bit where possible.
Current query statement is:
SELECT SorDetail.MStockCode,
SUM(SorDetail.MBackOrderQty) AS 'SumMBackOrderQty',
SUM(InvWarehouse.QtyOnHand) AS 'SumQtyOnHand'
FROM SysproCompanyJ.dbo.SorMaster SorMaster,
SysproCompanyJ.dbo.SorDetail SorDetail LEFT OUTER JOIN SysproCompanyJ.dbo.InvWarehouse InvWarehouse
ON SorDetail.MStockCode = InvWarehouse.StockCode
WHERE SorMaster.SalesOrder = SorDetail.SalesOrder
AND SorMaster.ActiveFlag != 'N'
AND SorDetail.MBackOrderQty > '0'
AND SorDetail.MPrice > '0'
GROUP BY SorDetail.MStockCode
ORDER BY SorDetail.MStockCode ASC
Without providing the complete picture, in terms of your RDBMS, database schema, a description of the problem you're trying to solve and sample data that matches the aforementioned, the following is just an illustration of what a solution based on Barmar's comment could look like:
SELECT SD.MStockCode,
SD.SumBackOrderQty,
IW.SumQtyOnHand
FROM (SELECT MStockCode,
SUM(MBackOrderQty) AS `SumBackOrderQty`
FROM SorDetail
JOIN SorMaster ON SorDetail.SalesOrder=SorMaster.SalesOrder
WHERE SorMaster.ActiveFlag != 'N'
AND SorDetail.MBackOrderQty > 0
AND SorDetail.MPrice > 0
GROUP BY MStockCode) AS SD
LEFT JOIN (SELECT MStockCode,
SUM(QtyOnHand) AS `SumQtyOnHand`
FROM InvWarehouse
GROUP BY MStockCode) AS IW ON SD.MStockCode=IW.MStockCode
ORDER BY SD.MStockCode;
Here's one approach:
select MStockCode,
(select sum(MBackOrderQty) from sorDetail as T2
where T2.MStockCode = T1.MStockCode ) as SumBackOrderQty,
(select sum(QtyOnHand) from invWarehouse as T3
where T3.MStockCode = T1.MStockCode ) as SumQtyOnHand
from
(
select mstockcode from sorDetail
union
select mstockcode from invWarehouse
) as T1
In a fiddle here: http://sqlfiddle.com/#!9/fdaca/6
Though my SumQtyOnHand values don't match yours (as #Gordon pointed out).

MS Access SQL query from 3 tables

I have 3 tables shown below in MS Access 2010:
Table: devices
id | device_id | Company | Version | Revision |
-----------------------------------------------
1 | dev_a | Almaras | 1.5.1 | 0.2A |
2 | dev_b | Enigma | 1.5.1 | 0.2A |
3 | dev_c | Almaras | 1.5.1 | 0.2C |
*Field: device_id is Primary Key Unique String
*Field ID is just an auto-number column
Table: activities
id | act_id | act_date | act_type | act_note |
------------------------------------------------
1 | dev_a | 07/22/2013 | usb_axc | ok |
2 | dev_a | 07/23/2013 | usb_axe | ok | (LAST ROW for dev_a)
3 | dev_c | 07/22/2013 | usb_axc | ok | (LAST ROW for dev_c)
4 | dev_b | 07/21/2013 | usb_axc | ok | (LAST ROW for dev_b)
*Field: act_id contains device_id; NOT UNIQUE
*Field ID is just an auto-number column
Table: matrix
id | mat_id | tc | ts | bat | cycles |
-----------------------------------------
1 | dev_a | 2811 | 10 | 99 | 200 |
2 | dev_a | 2911 | 10 | 97 | 400 |
3 | dev_a | 3007 | 10 | 94 | 600 |
4 | dev_a | 3210 | 10 | 92 | 800 | (LAST ROW for dev_d)
5 | dev_b | 1100 | 5 | 98 | 100 |
6 | dev_b | 1300 | 8 | 93 | 200 |
7 | dev_b | 1411 | 11 | 90 | 300 | (LAST ROW for dev_b)
8 | dev_c | 4000 | 27 | 77 | 478 | (LAST ROW for dev_c)
*Field: mat_id contains device_id; NOT UNIQUE
*Field ID is just an auto-number column
Is there any way to query tables to get results as shown below (each device from devices and only last row added [see example output table] from each of the other two tables):
Query Results:
device_id | Company | act_date | act_type | bat | cycles |
------------------------------------------------------------
device_a | Almaras | 07/23/2013 | usb_axe | 92 | 800 |
device_b | Enigma | 07/21/2013 | usb_axc | 90 | 300 |
device_c | Almaras | 07/22/2013 | usb_axc | 77 | 478 |
Any ideas? Thank you in advance for reading and helping me out :)
I think is what you want,
SELECT a.device_id, a.Company,
b.act_date, b.act_type,
c.bat, c.cycles
FROM ((((devices AS a
INNER JOIN activities AS b
ON a.device_id = b.act_id)
INNER JOIN matrix AS c
ON a.device_id = c.mat_id)
INNER JOIN
(
SELECT act_id, MAX(act_date) AS max_date
FROM activities
GROUP BY act_id
) AS d ON b.act_id = d.act_id AND b.act_date = d.max_date)
INNER JOIN
(
SELECT mat_id, MAX(tc) AS max_tc
FROM matrix
GROUP BY mat_id
) AS e ON c.mat_id = e.mat_id AND c.tc = e.max_tc)
The subqueries: d and e separately gets the latest row for every act_id.
Try
SELECT devices.device_id, devices.Company, activities.act_data, activities.act_type, matrix.bat, matrix.cycles
FROM devices
LEFT JOIN activities
ON devices.device_id = activities.act_id
LEFT JOIN matrix
ON devices.device_id = matrix.mat_id;
What do you consider the "last" row in Matrix?
You need to do something like
WHERE act_date in (SELECT max(a.act_date) from activities a where a.mat_id=d.device_id GROUP BY a.mat_id)
and something similar for the join to matrix.