Most efficient way to join fields in a dynamic table structure - sql

We have three tables storing document information that I'll call table A, B, and C. Table A stores the main fields for said documents (such as the file name and size and other general information). In order to dynamically add new fields, we use Table B to store a list of new fields and their data types(determined by an enumeration), and Table C to store the values for those fields.
Table A
ID - PK int
FieldX...
FieldY...
FieldZ...
Table B
Id - PK int
Name - nvarchar(max)
Type - int
Table C
Id - PK int
AId - FK to table A int
BId - FK to table B int
Value - nvarchar(max)
What we want to do as efficiently as possible is to pull the values from table C as an additional column in a query from table A. Right now here is how I'm doing it for each extra field in Table B.
select (select c.value
from B b, C c
where c.AId = a.Id
and c.Bid = b.Id
and b.Name = 'Dynamic Field Name') As 'Dynamic Field Name'
From A a
We have a database with Table A filled with 36000 rows. For each additional dynamic field that is added to the query, its adding about 4 seconds to the total time to finish the query, making a query that contains 10 dynamic fields take at least 40 seconds. Realistically, we want any query to finish in under 10. Is there any way to write this more efficiently?

First of all your query may give you errors because you have not defined C.(A+B) as unique. If there are multiple values per A+B combination in C, it causes the subquery to fail with
Msg 512, Level 16, State 1, Line 1
Subquery returned more than 1 value.
Assuming it is unique, then you should be safe to just LEFT JOIN them together to make use of SET-based operations.
select a.*,
c1.value [Dynamic Field Name 1],
c2.value [Dynamic Field Name 2]
From A a
JOIN B b1 on b1.Name = 'Dynamic Field Name1'
LEFT JOIN C c1 on c1.AId = a.Id and c.Bid = b1.Id
JOIN B b2 on b2.Name = 'Dynamic Field Name2'
LEFT JOIN C c2 on c2.AId = a.Id and c2.Bid = b2.Id
Using a subquery at the SELECT level forces each record, and each dynamic field to execute as a mini-select = very slow.

Perhaps a query like this would work as well:
select a.id
,MAX(CASE WHEN b.Name = 'Dynamic Field Name' THEN c.value END) As Dynamic_Field_Name
,MAX(CASE WHEN b.Name = 'Dynamic Field Name 2' THEN c.value END) As Dynamic_Field_Name2
from A a, B b, C c
where c.AId = a.Id
and c.Bid = b.Id
and b.Name IN ('Dynamic Field Name', 'Dynamic Field Name 2')
group by a.id
Same idea, you only want to make one pass through the table, rather than a lookup for each row. The aggregate is to compress the sparse matrix into a single row (ie there will only by one dynamic field value in a row, each row having a different column with a value). Don't know how it would perform though.

Related

Multiple JOINS in SQL with dataset

I have 3 tables that I need to JOIN in SQL.
Table A: Contains Employee ID
Table B: Contains Employee ID AND fica number
Table C: Contains fica number
Table A and B both contain an employee ID column. Table B and C both contain a column that has an employee's fica number. I need to find out all of the employees that are present in table C but not in table A. I wanted to do that by joining the fica number from table A and table A, then from there I was going to match up the employee ID that correlates to the fica number and try and JOIN that with table A. I try writing the syntax like this:
select distinct(a.employee_id), c.FICA_NBR, b.first_name, b.last_name from benefit A
join B on b.employee_id = a.employee_id
left join C on c.FICA_NBR = b.FICA_NBR
where c.FICA_NBR IS NULL
order by a.employee_id;
How would I go about editing this syntax?
select distinct a.employee_id, c.FICA_NBR, b.first_name, b.last_name from C
join B on c.FICA_NBR = b.FICA_NBR
left join benefit A on b.employee_id = a.employee_id
where A.employee_id IS NULL
order by a.employee_id;
The above code should help you achieve the goal since your condition is data is not present in table a, then its primary key should be null.
Hope I answered your question

count of record in one table not exist in multiple tables

I have a station tables consider table A,table B, table C,table D;
consider these table have some production data and consider there tables as a my production stations and in each station table i have a single column status which shows the flag 1,2
and in each table there is 1 primary number which is my serial no;
the flag 1 defines "OK" product and flag 2 defines "Fail Product";
but if the product get failed in table A then it may be reworked and introduced to production table A,B & C and that can be may make flag 1 in that station.
now i want to display the product which is failed in Table A and not further processed in table B and Table C.
means I want to display the total scrap product which is not processed in any station once it is failed
I have taken the data of failed product from each station into another table
and then i am comparing that serial no with each station table with flag 1
but not able to find exact how much count of scrap serial no's from each table
In Pseudo-SQL:
Select count(serial_no)
from table "Failedparts"
where /* serial_no neither in table "b" nor in table "c" with status = 1 */
;
From your verbal description it is a bit unclear which state you wish to query:
a failure on station A without subsequent production attempts ( disregarding their outcome )
or
a failure on station A with subsequent failures on both, station B and C
The following queries operate on the original tables according to your verbal description. It is unclear how exactly you filled table "Failedparts" ( your description suggests that you loaded failed attempts from each table A, B, C ).
Case 1. would translate into
SELECT count(a.serial_no)
FROM table_a a
WHERE a.status = 2
AND NOT EXISTS ( SELECT 1 FROM table_b b WHERE b.serial_no = a.serial_no )
AND NOT EXISTS ( SELECT 1 FROM table_c c WHERE c.serial_no = a.serial_no )
;
For case 2., use:
SELECT count(a.serial_no)
FROM table_a a
JOIN table_b b ON ( b.serial_no = a.serial_no AND b.status = 2 )
JOIN table_c c ON ( c.serial_no = a.serial_no AND c.status = 2 )
WHERE a.status = 2
;
(Possibly you aim at a slightly different notion, but the above queries should get you started).
Literally as you commented in the query stub
Select count(serial_no)
from table "Failedparts" a
where /* serial_no neither in table "b" nor in table "c" with status = 1 */
not exists (select 1 from table_b b where b.serial_no = a.serial_no and b.flag = 1)
and not exists (select 1 from table_c c where c.serial_no = a.serial_no and c.flag = 1);

Selecting full names of pairs of ISO-coded countries

Suppose I have got two tables, one of which is named A and contains two columnsĀ : country1 (as an ISO code), country2 (as an ISO code) whilst the other one is named B and contains two columnsĀ : code (ISO code of country) and name (full name of country).
I would like to list all pairs of countries that occur in table A using their full names, rather than their ISO codes.
If I had to get the full name of every country that occurs in the first column of table A, I would write te following:
SELECT name AS name1
FROM B
INNER JOIN A ON B.code = A.country1
However, I am struggling to find out how to get both columns with full names.
You should be able to join the code column from B on both the country1 and country2 columns from A. You just need to make sure you alias it differently on each join.
SELECT A.country1, A.country2, B1.name AS name1, B2.name as name2
FROM A
JOIN B AS B1 ON B1.code = A.country1
JOIN B AS B2 ON B2.code = A.country2
Join should always be on same column type from both tables. Isnull will work for you, If country codes are stored in different columns in TableA
SELECT name AS name1
FROM B
INNER JOIN A ON B.code = isnull(A.country1, a.Country2)
or Use case statement
SELECT name AS name1
FROM B
INNER JOIN A ON B.code = case when A.country1 is null then A.country2
else A.country1 end

SQL query construction: checking if query result is subset of another

Hi Guys I have a table relation which works like this (legacy)
A has many B and B has many C;
A has many C as well
Now I am having trouble coming up with a SQL which will help me to get all B (Id of B to make it simple) mapped to certain A(by Id) AND any B which has a collection of C that's a subset of Cs of that A.
I have failed to come up with a decent sql specially for the second part and was wondering if I can get any tips / suggestions re how I can do that.
Thanks
EDIT:
Table A
Id |..
------------
1 |..
Table B
Id |..
--------------
2 |..
Table A_B_rel
A_id | B_id
-----------------
1 | 2
C is a strange table. The data of C (single column) is actually just duped in 2 rel table for A and B. so its like this
Table B_C_Table
B_Id| C_Value
-----------------
2 | 'Somevalue'
Table A_C_Table
A_Id| C_Value
-------------
1 | 'SomeValue'
So I am looking for Bs the C_Values of which are subset of certain A_C_Values.
Yes, the second part of your problem is a bit tricky. We've got B_C_Table on the one hand, and a subset of A_C_Table where A_ID is a specific ID, on the other.
Now, if we use an outer join, we'll be able to see which rows in B_C_Table have no match in A_C_Table:
SELECT *
FROM B_C_Table bc
LEFT JOIN A_C_Table ac ON bc.C_Value = ac.C_Value AND ac.A_ID = #A_ID
Note that it is important to put the ac.A_ID = #A_ID into the ON clause rather than into WHERE, because in the latter case we would be filtering out non-matching rows of #A_ID, which is not what we want.
The next step (to achieving the final query) would be to group rows by B and count rows. Now, we will calculate both the total number of rows and the number of matching rows.
SELECT
bc.B_ID,
COUNT(*) AS TotalCount,
COUNT(ac.A_ID) AS MatchCount
FROM B_C_Table bc
LEFT JOIN A_C_Table ac ON bc.C_Value = ac.C_Value AND ac.A_ID = #A_ID
GROUP BY bc.B_ID
As you can see, to count matches, we simply count ac.A_ID values: in case of no match the corresponding column will be NULL and thus not counted. And if indeed some rows in B_C_Table do not match any rows in the subset of A_C_Table, we will see different values of TotalCount and MatchCount.
And that logically leads us towards the final step: comparing those counts. (For, obviously, if we can obtain values, we can also compare them.) But not in the WHERE clause, of course, because aggregate functions aren't allowed in WHERE. It's the HAVING clause that is used to compare values of grouped rows, including aggregated values too. So...
SELECT
bc.B_ID,
COUNT(*) AS TotalCount,
COUNT(ac.A_ID) AS MatchCount
FROM B_C_Table bc
LEFT JOIN A_C_Table ac ON bc.C_Value = ac.C_Value AND ac.A_ID = #A_ID
GROUP BY bc.B_ID
HAVING COUNT(*) = COUNT(ac.A_ID)
The count values aren't really needed, of course, and when you drop them you will be able to UNION the above query with the one selecting B_ID from A_B_rel:
SELECT B_ID
FROM A_B_rel
WHERE A_ID = #A_ID
UNION
SELECT bc.B_ID
FROM B_C_Table bc
LEFT JOIN A_C_Table ac ON bc.C_Value = ac.C_Value AND ac.A_ID = #A_ID
GROUP BY bc.B_ID
HAVING COUNT(*) = COUNT(ac.A_ID)
Sounds like you need to think in terms of double negation, i.e. there should not exist any B_C that does not have a matching A_C (and I'm guessing there should be at least one B_C).
So, try something like
select B.B_id
from Table_B B
where exists (select 1 from B_C_Table BC
where BC.B_id = B.B_id)
and not exists (select 1 from B_C_Table BC
where BC.B_id = B.B_id
and not exists(select 1 from B_C_Table AC
join A_B_Rel ABR on AC.A_id = ABR.A_id
where ABR.B_id = B.B_id
and BC.C_Value = AC.C_Value))
Perhaps this is what you're looking for:
SELECT B_id
FROM A_B_rel
WHERE A_id = <A ID>
UNION
SELECT a.B_Id
FROM B_C_Table a
LEFT JOIN A_C_Table b ON a.C_Value = b.C_Value AND b.A_Id = <A ID>
GROUP BY a.B_Id
HAVING COUNT(CASE WHEN b.A_Id IS NULL THEN 1 END) = 0
The first SELECT gets all B's which are mapped to a particular A (<A ID> being the input parameter for the A ID), then we tack onto that result set any additional B's whose entire set of C_Value's are within the subset of the C_Value's of the particular A (again, <A ID> being the input parameter).

Fetching 3 tables at a time, retrieve only the table which matches the condition (SQL)

I have 3 tables: WATER_TENDER,ENGINE and TRAILER.
All of them have the EQ_ID as primary key and some other attributes, different for each table.
for example, EQ _ ID='WT-123' points a row inside the WATER _TENDER table only, and nothing inside the other tables.
As I do not know the EQ _ID in advance so I do not know which table to look at, how can I write a single query which checks all the 3 tables based on EQ _ID and retrieve just the table which matches the EQ _ID specified??
If you decide to tackle the model a bit, you could use:
SELECT Type, a ,b ,c ,d ,e ,f ,g ,h
FROM Equipment AS eq
LEFT JOIN ENGINE AS en ON en.EQ_ID = eq.EQ_ID
LEFT JOIN TRAILER AS tr ON tr.EQ_ID = eq.EQ_ID
LEFT JOIN WATER_TENDERER AS wt ON wt.EQ_ID = eq.EQ_ID
WHERE eq.EQ_ID = 'WT-123'
You can use UNION ALL to select rows from all tables and select all columns, providing NULL when that column doesn't exist:
SELECT a, b, c, d, e, f FROM (
SELECT a, b, NULL AS c, NULL AS d, NULL AS e, NULL AS f FROM WATER_TENDER
UNION ALL
SELECT NULL AS a, NULL AS b, c, d, NULL AS e, NULL AS f FROM ENGINE
UNION ALL
SELECT NULL AS a, NULL AS b, NULL AS C, NULL AS D, e, f FROM TRAILER) AS T1
WHERE _ID = #id
It's a bit dirty though, IMHO. It feels like there's something wrong with your design if you have to do this.
I don't think you can do that in a normal select query. If possible, I'd recommend either:
performing some logic so that you know which table you're supposed to look in (i.e., a 'WT-' means you should look in the WATER_TENDER table)
performing 3 separate queries, 1 for each table
redesigning your database (possibly have a master table that stores the relationships between an id and records the other tables)