MS Access (via VB.NET) - convert linked attribute tables to columns in output - sql

I've searched, and found a lot of similar things, but not quite what I'm looking for.
I have what are essentially a bunch of linked tables with attributes and values.
xxBio table
xxBio_ID xxBio_LINK
1 100
2 101
xx table
xxBio_LINK xxAttr_LINK
100 1000
101 2000
xxAttr table
xxAttr_LINK xxAttrCat_1_LINK xxAttrCat_2_LINK
1000 null 550
2000 650 null
xxAttrCat_1 table
xxAttrCat_1_LINK xxAttrCat_1_Description xxAttrCat_1_Value
650 wumpus 20
xxAttrCat_2 table
xxAttrCat_2_LINK xxAttrCat_2_Description xxAttrCat_2_Value
550 frith 30
OUTPUT NEEDS TO BE:
xxBio_ID frith wumpus
1 30 null
2 null 20
I can easily see how to get the result set with columns like attribute_name1, attribute_value1, attribute_name2, attribute_value2, and so forth.
SELECT xxBio.ID, xxAttrCat_1.xxAttrCat_1_Description, xxAttrCat_1.xxAttrCat_1_Value, xxAttrCat_2.xxAttrCat_2_Description, xxAttrCat_2.xxAttrCat_2_Value
FROM ((
(xx INNER JOIN xxBio ON xx.xxBio_LINK = xxBio.xxBio_LINK)
INNER JOIN xxAttr ON xx.xxAttr_LINK = xxAttr.xxAttr_LINK)
LEFT JOIN xxAttrCat_1 ON xxAttr.xxAttrCat_1_LINK = xxAttrCat_1.xxAttrCat_1_LINK)
LEFT JOIN xxAttrCat_2 ON xxAttr.xxAttrCat_2_LINK = xxAttrCat_2.xxAttrCat_2_LINK
But that's not what we need. We need the attribute names to be column names.
How do we achieve this?
Update to question:
It turned out that we misunderstood the requirements, so we had to take a different route and did not get to try the answers. I appreciate the help.

Put the SQL in a string and execute from VBA. (Dynamic SQL?)
sql = "SELECT xxBio.ID, " & xxAttrCat_1.xxAttrCat_1_Description & ", " & ...

Consider using IIF() conditional statements:
SELECT xxBio.ID,
IIF(xxAttrCat_1.xxAttrCat_1_Description = 'wumpus',
xxAttrCat_1.xxAttrCat_1_Value, NULL) As [wumpus],
IIF(xxAttrCat_2.xxAttrCat_2_Description = 'frith',
xxAttrCat_2.xxAttrCat_2_Value, NULL) As [frith]
FROM (((xx
INNER JOIN xxBio ON xx.xxBio_LINK = xxBio.xxBio_LINK)
INNER JOIN xxAttr ON xx.xxAttr_LINK = xxAttr.xxAttr_LINK)
LEFT JOIN xxAttrCat_1 ON xxAttr.xxAttrCat_1_LINK = xxAttrCat_1.xxAttrCat_1_LINK)
LEFT JOIN xxAttrCat_2 ON xxAttr.xxAttrCat_2_LINK = xxAttrCat_2.xxAttrCat_2_LINK
If each ID can have multiple values along same descriptions and you need to sum or average all corresponding values, then turn query into conditional aggregation (known as pivoting). Below also uses table aliases, a, b, c1, c2 for brevity and organization.
SELECT b.ID,
SUM(IIF(c1.xxAttrCat_1_Description='wumpus', c1.xxAttrCat_1_Value, NULL)) As [wumpus],
SUM(IIF(c2.xxAttrCat_2_Description='frith', c2.xxAttrCat_2_Value, NULL)) As [frith]
FROM (((xx
INNER JOIN xxBio b ON xx.xxBio_LINK = b.xxBio_LINK)
INNER JOIN xxAttr a ON xx.xxAttr_LINK = a.xxAttr_LINK)
LEFT JOIN xxAttrCat_1 c1 ON a.xxAttrCat_1_LINK = c1.xxAttrCat_1_LINK)
LEFT JOIN xxAttrCat_2 c2 ON a.xxAttrCat_2_LINK = c2.xxAttrCat_2_LINK
GROUP BY b.ID
And if such descriptions can be many use MS Access SQL's crosstab query. But since you have two categories, run a crosstab on each and save them as stored queries or views. Then, join together:
--CATEGORY 1
TRANSFORM SUM(c1.xxAttrCat_1_Value) AS SumOfValue
SELECT b.ID,
FROM ((xx
INNER JOIN xxBio b ON xx.xxBio_LINK = b.xxBio_LINK)
INNER JOIN xxAttr a ON xx.xxAttr_LINK = a.xxAttr_LINK)
LEFT JOIN xxAttrCat_1 c1 ON a.xxAttrCat_1_LINK = c1.xxAttrCat_1_LINK
GROUP BY b.ID
PIVOT c1.xxAttrCat_1_Description;
--CATEGORY 2
TRANSFORM SUM(c2.xxAttrCat_2_Value) AS SumOfValue
SELECT b.ID,
FROM ((xx
INNER JOIN xxBio b ON xx.xxBio_LINK = b.xxBio_LINK)
INNER JOIN xxAttr a ON xx.xxAttr_LINK = a.xxAttr_LINK)
LEFT JOIN xxAttrCat_2 c2 ON a.xxAttrCat_2_LINK = c2.xxAttrCat_2_LINK
GROUP BY b.ID
PIVOT c2.xxAttrCat_2_Description;
--CROSSTAB QUERIES JOIN
SELECT c1.*, c2.*
FROM Categ1CrossTabQ c1
INNER JOIN Categ1CrossTabQ c2
ON c1.ID = c2.ID;
Please note: comments above with double dash are not allowed in an Access query and only one statement is allowed in saved query. Access has a limit of 255 columns per table/query, so pivoted values greater than 255 will err out unless you use IN clause. Finally, you cannot use crosstabs as subqueries. And crosstabs are strictly an MS Access command and not used in other DBMS's.

Related

How to subtract from another table in SQL

SELECT
COUNT(ca.Plate) as 'OccupiedElectricSlots'
FROM cities C
JOIN ParkingHouses HS on C.Id = hs.CityId
JOIN ParkingSlots PS on HS.Id = ps.ParkingHouseId
LEFT JOIN Cars Ca on PS.Id = Ca.ParkingSlotsId
WHERE ps.ElectricOutlet = 1
GROUP BY hs.HouseName, C.CityName
SELECT
MAX(Ps.SlotNumber) as 'ParkingSlotTotal'
,MAX(PS.SlotNumber) - Count(ca.Plate) as 'FreeSlots'
,SUM(CAST(PS.ElectricOutlet AS INT)) as 'ElectricOutlet'
,Hs.HouseName
,C.CityName
FROM Cities C
JOIN ParkingHouses HS on C.Id = hs.CityId
JOIN ParkingSlots PS on HS.Id = ps.ParkingHouseId
LEFT JOIN Cars Ca on PS.Id = Ca.ParkingSlotsId
GROUP BY hs.HouseName, C.CityName
How can I subtract the first tables numbers on the second one?
I want to see how many free slots that have electric outlet.
Like this Column ElectricOutlet - OccupiedElectricSlots = result
I'm quite new at SQL, but I have tried to outer apply (don't fully understand it), and I tried to join them both tables togheter. Tried different where conditions but I'm stuck atm.
Your queries are almost identical as far as I can see. You can change your first query to:
SELECT COUNT(CASE WHEN ps.ElectricOutlet = 1 THEN ca.Plate END) as 'OccupiedElectricSlots'
FROM cities C
JOIN ParkingHouses HS on C.Id = hs.CityId
JOIN ParkingSlots PS on HS.Id = ps.ParkingHouseId
LEFT JOIN Cars Ca on PS.Id = Ca.ParkingSlotsId
GROUP BY hs.HouseName, C.CityName
I.e., instead of filtering on ps.ElectricOutlet you just ignore those rows in COUNT. Now you can just:
SELECT
[...]
,SUM(CAST(PS.ElectricOutlet AS INT)) - COUNT(CASE WHEN ...) AS result
[...]
FROM Cities C
JOIN ParkingHouses HS
ON C.Id = hs.CityId
JOIN ParkingSlots PS
ON HS.Id = ps.ParkingHouseId
LEFT JOIN Cars Ca
ON PS.Id = Ca.ParkingSlotsId
GROUP BY hs.HouseName, C.CityName
The MINUS operator is used to subtract the result set obtained by first SELECT query from the result set obtained by second SELECT query.
MINUS compares the data in two tables and returns only the rows of data using the specified columns that exist in the first table but not the second.

SQL- basic but still finding it complicated

I am working on SQL query which should return the list of Managers and the staff who reports to them.
Unfortunately there is no separate table for Employee or Staff but a single 'resource' table called ahsresources.
The managers are identified with a relation called 'C0'.
Even after trying various Joins, I am unable to extract the list. The idea is that a manager will run the report to see his reportees, as well as those staff who report to his own reportees
Example -
Now, if lets say HDY is running the query, then its should return him the below result
Below is the query I have created, but for the matter of understanding the issue, you can use the above example.
select a.description as manager1,a.rel_value as MGID,a.resource_id as Reportee1_MGR2,r.name,a.date_to as date, r.date_to,a1.resource_id as MG3ID,r1.name as Rep3Name,
a2.resource_id as MG4ID,r2.name as Rep4Name
from ahsrelvalue a
LEFT OUTER JOIN ahsresources r
ON r.resource_id = a.resource_id and r.client = a.client and a.date_to='12/31/2099'
LEFT OUTER JOIN ahsrelvalue a1
ON a1.rel_Value = a.resource_id and a1.client = a.client and a1.date_to = '12/31/2099'
LEFT OUTER JOIN ahsrelvalue a2
ON a2.rel_Value = a1.resource_id and a2.client = a1.client and a2.date_to = '12/31/2099'
LEFT OUTER JOIN ahsresources r1
ON r1.resource_id = a1.resource_id and r1.client = a1.client and a1.date_to='12/31/2099'
LEFT OUTER JOIN ahsresources r2
ON r2.resource_id = a2.resource_id and r2.client = a2.client and a2.date_to='12/31/2099'
where a.rel_Value = '$?resid' and a.rel_attr_id='C0' and r.date_to = '12/31/2099' and r1.date_to ='12/31/2099'
and r.status !='C' and r1.status!='C' and r2.status!='C'
In SQL Server, you can use a recursive query to traverse this hierarchical dataset:
with cte as (
select t.* from mytable where managerID = 6
union all
select t.*
from cte c
inner join mytable t on t.managerID = c.staffID
)
select * from cte

Stored Procedure (T-SQL) Join multiple tables

I'm currently working on a t-sql query in Microsoft SQL Server Management Studio (SQL Server), which should gather data over several tables. In the end I'll need [OfferId] and [Label]. Do you have an idea on how to write those 4 query statements into 1?
SELECT a.OfferId AS [OfferId], a.OfferDataId AS [OfferDataId], b.DeliveryModelPoolId AS [DeliveryModelPoolId]
FROM [Offer].[Offer] a
INNER JOIN [Offer].[OfferData] b
ON a.OfferDataId = b.OfferDataId
OfferId | OfferDataId | DeliveryModelPoolId
1..........| 1..................| 4
SELECT a.DeliveryModelPoolId AS [DeliveryModelPoolId], b.PoolId AS [PoolId]
FROM [Offer].[OfferData] a
INNER JOIN [Offer].[Pool] b
ON a.DeliveryModelPoolId = b.PoolId
DeliveryModelPoolId | PoolId
4................................| 4
SELECT a.DeliveryModelId AS [DeliveryModelId]
FROM [Offer].[Delivery] a
INNER JOIN [Offer].[Pool] b
ON a.DeliveryModelPoolId = b.PoolId
DeliveryModelId
2
6
SELECT a.Label AS [Label]
FROM [Offer].[DeliveryModel] a
INNER JOIN [Offer].[DeliveryLabels] b
ON a.DeliveryModelId = b.DeliveryModelId
Label
Service Center
Delivery By Car
Thanks a lot! :)
If you are planning to reuse the query, I would put it into a view:
CREATE VIEW vWOfferData
AS
SELECT a.offerID, dl.label
FROM [Offer].[Offer] a
INNER JOIN [Offer].[OfferData] b
ON a.OfferDataId = b.OfferDataId
INNER JOIN [Offer].[Pool] p2
ON b.DeliveryModelPoolId = p2.PoolId
INNER JOIN [Offer].[Delivery] d3
ON d3.DeliveryModelPoolId = p2.PoolId
INNER JOIN [Offer].[DeliveryModel] dl
ON dl.DeliveryModelId = d3.DeliveryModelId
You can then use it as a table. For example:
SELECT * FROM vWOfferData
You can join more than once in a query
select a.offerID, dl.label
FROM [Offer].[Offer] a
INNER JOIN [Offer].[OfferData] b
ON a.OfferDataId = b.OfferDataId
inner join [Offer].[Pool] p2
ON b.DeliveryModelPoolId = p2.PoolId
inner join [Offer].[Delivery] d3
ON d3.DeliveryModelPoolId = p2.PoolId
inner join [Offer].[DeliveryModel] dl
on dl.DeliveryModelId = d3.DeliveryModelId

SQL select results not appearing if a value is null

I am building a complex select statement, and when one of my values (pcf_auto_key) is null it will not disipaly any values for that header entry.
select c.company_name, h.prj_number, h.description, s.status_code, h.header_notes, h.cm_udf_001, h.cm_udf_002, h.cm_udf_008, l.classification_code
from project_header h, companies c, project_status s, project_classification l
where exists
(select company_name from companies where h.cmp_auto_key = c.cmp_auto_key)
and exists
(select status_code from project_status s where s.pjs_auto_key = h.pjs_auto_key)
and exists
(select classification_code from project_classification where h.pcf_auto_key = l.pcf_auto_key)
and pjm_auto_key = 11
--and pjt_auto_key = 10
and c.cmp_auto_key = h.cmp_auto_key
and h.pjs_auto_key = s.pjs_auto_key
and l.pcf_auto_key = h.pcf_auto_key
and s.status_type = 'O'
How does my select statement look? Is this an appropriate way of pulling info from other tables?
This is an oracle database, and I am using SQL Developer.
Assuming you want to show all the data that you can find but display the classification as blank when there is no match in that table, you can use a left outer join; which is much clearer with explicit join syntax:
select c.company_name, h.prj_number, h.description, s.status_code, h.header_notes,
h.cm_udf_001, h.cm_udf_002, h.cm_udf_008, l.classification_code
from project_header h
join companies c on c.cmp_auto_key = h.cmp_auto_key
join project_status s on s.pjs_auto_key = h.pjs_auto_key
left join project_classification l on l.pcf_auto_key = h.pcf_auto_key
where pjm_auto_key = 11
and s.status_type = 'O'
I've taken out the exists conditions as they just seem to be replicating the join conditions.
If you might not have matching data in any of the other tables you can make the other inner joins into outer joins in the same way, but be aware that if you outer join to project_status you will need to move the statatus_type check into the join condition as well, or Oracle will convert that back into an inner join.
Read more about the different kinds of joins.

SQL JOIN Condition moved to with where clause produces differences

Query 1
select count(1)
from sdb_snmp_sysdata s
left join sdb_snmp_entphysicaltable e on s.source = e.source **and e.class = 3**
left join SDB_DF_DEVICE_DNS dns on dns.source = s.source
left join sdb_fdb_node f on upper(f.oldnodeid) = upper(dns.dns_name)
where (regexp_like(s.descr, 'NFXS-F FANT-F ALCATEL-LUCENT|Motorola APEX3000')
or regexp_like(e.descr, 'Motorola BSR64000 HD 100A Redundant Chassis|AS2511-RJ chassis')
or trim(e.ModelName) in ('RFGW1', 'ARCT01949', 'ARCT03253', 'UBR10012', 'WS-C3750-48TS-S', 'WS-C3750V2-48TS-S')
or e.name like '%Nexus5596 Chassis%')
Query 2:
select count(1)
from sdb_snmp_sysdata s
left join sdb_snmp_entphysicaltable e on s.source = e.source
left join SDB_DF_DEVICE_DNS dns on dns.source = s.source
left join sdb_fdb_node f on upper(f.oldnodeid) = upper(dns.dns_name)
where (regexp_like(s.descr, 'NFXS-F FANT-F ALCATEL-LUCENT|Motorola APEX3000')
or regexp_like(e.descr, 'Motorola BSR64000 HD 100A Redundant Chassis|AS2511-RJ chassis')
or trim(e.ModelName) in ('RFGW1', 'ARCT01949', 'ARCT03253', 'UBR10012', 'WS-C3750-48TS-S', 'WS-C3750V2-48TS-S')
or e.name like '%Nexus5596 Chassis%') **and e.class = 3**
The above two queries return different number of rows by changing e.class condition from on clause to where clause. I am unable to figure out. any help is appreciated.
My Understanding:
query 1 left outer join between sysdata and entphysicaltable hash join happens after full scan of individual tables.
in the second query 2 join happens after entphysicaltable is reduced to records containing only entphysicaltable.class = 3.
to me the query makes same sense but returns different results.
I can relate to this question I would like to know a concrete reason.
The best explanation is on a little example. Let have two tables
TABLE A
C1
----------
1
2
TABLE B
C1 C2
---------- -
1 x
Then the query with the filter B.c2 = 'x' in the ON clause returns 2 rows
select *
from A left outer join B
on A.c1 = B.c1 and B.c2 = 'x';
C1 C1 C2
---------- ---------- --
1 1 x
2
while when the filter is moved in the WHERE clause, only one row is delivered
select *
from A left outer join B
on A.c1 = B.c1
WHERE B.c2 = 'x';
C1 C1 C2
---------- ---------- --
1 1 x
The WHERE clause simple overrules the OUTER JOIN row missing logik - wee all know that NULL is not equal 'x', so the second row is discarded.
BWT if you see in the old join syntax constructs like B.c2(+) = 'x' this is the very same thema.
If I read your question right, then it simply comes down to how a LEFT JOIN works.
The way a (outer) LEFT JOIN works is that it will join what's on your left side with what's on your right side.
And then it being an outer join it will try to add NULL values to the right, for the situation where there is no match on the right.
However, by you adding your constraints in the WHERE clause, you're telling the query engine to filter out the rows where there is NULL because they will not match your WHERE clause.
If you have the filters in your ON clause - the query engine will not remove/filter out the NULL rows.
This happens because the WHERE is 'executed' after the JOINs.
That's why you get different number of rows, because an OUTER join functions differently based on whether you use the ON or the WHERE clause.
So if you want the join to include NULL rows, you'll need to use the ON clause.