Optimize a JOIN query, for multiple correspondances between tables - sql

I am trying to optimize a function (MySQL), but the JOIN is still not completely understood (I try to make a simple example) :
I have 2 tables :
Table ITEMS
ID | ID_ORDER | ID_BOX | NAME
001 | 2564 | 123 | SPOON_1
002 | 2564 | 123 | SPOON_2
006 | 2564 | 123 | SHOES_2
007 | 2564 | 123 | SHOES_1
003 | 2565 | 124 | SPOON_1
004 | 2565 | 124 | SPOON_2
008 | 2565 | 124 | SHOES_1
009 | 2565 | 124 | SHOES_2
005 | 2565 | 125 | SPOON_1
010 | 2565 | 125 | SHOES_1
The description of objects are linked to the ITEM table by ID_CONTRACT, and NAME.(Not possible to have 2 items with same name inside the same contract).
Table DESCRIPTION :
ID_ORDER | NAME_ITEM | LENGTH | WIDTH | ....
2564 | SPOON_1 | 200 | 20 ...
2564 | SPOON_2 | 300 | 20 ...
2564 | SHOES_1 | 500 | 20 ...
2564 | SHOES_2 | 600 | 20 ...
Now, I need to know all items I have in the contract, with their description, and I use this query :
SELECT *,description.* FROM items INNER JOIN description
ON (description.ID_CONTRACT=items.ID_CONTRACT AND description.NAME_ITEM=items.NAME)
WHERE ID_CONTRACT= 2564
First, I just read it is not correct query (I need to copy all description fields by hand in query?), because ID_CONTRACT is in both tables, and sometimes it gives me mistake(sometimes not), and I read there that it is not possible to ignore duplicates.
Then I am wondering, as I make a select on ITEMS table, MySQL is looking for each line a correspondance in DESCRIPTION table?
Is there a way to optimize query (another kind of JOIN), so it will not search everytime in ITEMS table, when he meets 2 elements (or more) in ITEMS, with same ID_CONTRACT/NAME ?

select * mean select all the columns from all the tables
and in your case this is like select items.,description. so with the syntax SELECT ,description. seems you are trying to select two time the columns for table description
due the fact you have the same column name ID_CONTRACT in both table this produce an ambiguity on coumn name for the DB engine during the join.
for avoid this you simply need a full reference name in join columns name eg:
table1.col1 = table2.col1
this way the db engino know which column form each table must be use for join
SELECT items.*, description.*
FROM items
INNER JOIN description ON description.ID_CONTRACT=items.ID_CONTRACT
AND description.NAME_ITEM=items.NAME
WHERE ID_CONTRACT= 2564
for your second part of question
MySQL is looking for each line a correspondance in DESCRIPTION table?
yes.
A relation db work on sets of data and retrieve all the correspondance between the tables
if really the rows are duplicated you could retrive the distinct result using
select DISTICNT col1, col2..
tipically a select * from a join on correctly normalized data set don't produce dulicated rows (at least you value in one column differ beetween the rows)
but if some column result are not important for you and can be omittedc form the result, this case can produce a result with duplicated row and you can perform a selective select using only the column name you really need and apply the disctint clause

Related

SQL Server selecting data as array from two tables

I have a database in which there are two tables tableA, tableB. Now for each primary id in tableA there may be multiple rows in tableB.
Table A primary key (ServiceOrderId)
+----------------+-------+-------+-------------+
| ServiceOrderId | Tax | Total | OrderNumber |
+----------------+-------+-------+-------------+
| 12 | 45.00 | 347 | 1011 |
+----------------+-------+-------+-------------+
Table B foreign key (ServiceOrderId)
+----+-------------+---------------------+----------+-------+------+----------------+
| Id | ServiceName | ServiceDescription | Quantity | Price | Cost | ServiceOrderId |
+----+-------------+---------------------+----------+-------+------+----------------+
| 39 | MIN-C | Commercial Pretreat | NULL | 225 | 23 | 12 |
+----+-------------+---------------------+----------+-------+------+----------------+
| 40 | MIN-C | Commercial Pretreat | NULL | 225 | 25 | 12 |
+----+-------------+---------------------+----------+-------+------+----------------+
Is there a way in which I can fetch the values as an array of multiple rows of tableB with single row of tableA. Because when I am saving to database I am using temp table to save multiple rows of tableB with single row of tableA.
Query I am using
SELECT
ordr.*,
info.*
FROM
tblServiceOrder as ordr
JOIN
tblServiceOrderInfo as info ON ordr.ServiceOrderId = info.ServiceOrderId
But above query is giving two rows for each ServiceOrderId. I am using node api to fetch data. I want something like;
Object:{
objectA:{id:12,tax:45.00:total:347,ordernumber:1011},
objectB:[
{id:39,servicename:'MIN-C',description:'Commercial Pretreat',Quantity :NULL,Price:225,Cost:23,ServiceOrderId:12 },
{id:40,servicename:'MIN-C',description:'Commercial Pretreat',Quantity :NULL,Price:225,Cost:25,ServiceOrderId:12}
]
}
There are several solutions. The first one is to use your SELECT, but with adding ORDER BY ServiceOrderID and when data are converting to object, to use the first row only in the loop for new ServiceOrderId from ordr table and add every row for the data from info table.
Other possibility is to select data from ordr table only and for every row to make another select by ServiceOrderId from info table. This solution should not be used for huge tables.

Empty row in Teradata query result

I know this might sound weird and but after executing a Teradata query, I am getting the result as such:
| ID | Name |
+------+---------+
| 1007 | Raj |
| | |
| 1001 | Sanjib |
| 1008 | Suman |
| 1004 | Mohan |
The 2nd row is just blank. (Sorry, could not edit properly but I hope you get the point) The query is pretty simple.
SELECT DISTINCT column_names
FROM table1
FULL OUTER JOIN table2 ON table1.ID = table2.ID;
I do not have access to its' DDL statements.
There was also another scenario where this same table was in the output, just without any row elements, just the column names.
I am using SQL WORKBENCH/J. Am I missing something?

Oracle SQL Select Unique Value AND Only one value from matching rows with non-unique values

I have two tables, a master table and a general information table. I need to update my master table from the general table. How can I update the master table when the general info table can have slightly different values for the descriptions?
Master
+------+---------+
| Code | Desc |
+------+---------+
| 156 | Milk |
| 122 | Eggs |
| 123 | Diapers |
+------+---------+
Info
+------+---------------+--------+
| Code | Desc | Price |
+------+---------------+--------+
| 156 | Milk | $3.00 |
| 122 | Eggs | $2.00 |
| 123 | Diapers | $15.00 |
| 124 | Shopright Cola| $2.00 |
| 124 | SR Cola | $2.00 |
+------+---------------+--------+
As you can see item 124 has 2 descriptions. It does not matter which description.
My attempt is returning 124 with both codes, I understand my code is looking for both the unique Code and description in the master which is why it returns both 124 but I'm unsure how to fix it.
INSERT INTO MASTER
(
SELECT UNIQUE(Code), Desc FROM INFO A
WHERE NOT EXISTS
(SELECT Code FROM MASTER B
WHERE A.Code = B.Code )
);
I have also tried:
INSERT INTO MASTER
(
SELECT UNIQUE(PROC_CDE), Desc FROM FIR_CLAIM_DETAIL A
WHERE Code NOT IN
(SELECT Code FROM FIR_CODE_PROC_CDE_MSTR B
WHERE A.Code = B.Code )
);
Unique filters the duplicated entries in the SELECTed result set across all columns, not just one key.
When you want to extract the other attributes of a key you filtered, you have to instruct the database to first group the unique keys. To choose one of attributes of a grouped key, we can use an AGGREGATE function. Like MAX(), MIN().
INSERT INTO MASTER
(
SELECT PROC_CDE, MAX(Desc) FROM FIR_CLAIM_DETAIL A
WHERE Code NOT IN
(SELECT Code FROM FIR_CODE_PROC_CDE_MSTR B
WHERE A.Code = B.Code )
GROUP BY PROC_CDE
);
There're analytical functions which can be used for even complex requirements.

order by case using SQL

I have the following table:
+----+----------+-----+-----------+----------+
| 1 | Ramesh | 32 | Ahmedabad | 2000.00 |
| 7 | Muffy | 24 | Indore | 10000.00 |
| 6 | Komal | 22 | MP | 4500.00 |
| 2 | Khilan | 25 | Delhi | 1500.00 |
| 3 | kaushik | 23 | Kota | 2000.00 |
| 5 | Hardik | 27 | Bhopal | 8500.00 |
| 4 | Chaitali | 25 | Mumbai | 6500.00 |
+----+----------+-----+-----------+----------+
And, I am using the following query to sort the table as per my preferred order, but I am getting the following SQL error:
CURSOR OPERATION CONFLICT
My query:
SELECT * FROM CUSTOMERS ORDER BY (CASE ADDRESS WHEN 'DELHI' THEN 1
WHEN 'BHOPAL' THEN 2
WHEN 'KOTA' THEN 3
WHEN 'AHMADABAD' THEN 4
WHEN 'MP' THEN 5
ELSE 100 END) ASC, ADDRESS DESC
Since Access does not suport CASE...WHEN, you have two options to get a custom sort order.
1) If you can edit the database structure, add a "SortOrder" column to the table.
Assuming the "Address" field has unique values in the "CUSTOMERS" table, create a field in the "CUSTOMERS" table called "SortOrder", specifying "1" for "Delhi", "2" for "Bhopal", and so on:
SELECT *
FROM CUSTOMERS
ORDER BY CUSTOMERS.SortOrder DESC;
However, if it"s possible for the same "Address" value to appear multiple times in the "CUSTOMERS" table, you should create a new table like "ADDRESSES" that contains the Address and SortOrder values. The SQL would ultimately look like this:
SELECT CUSTOMERS.*
FROM CUSTOMERS INNER JOIN ADDRESSES ON CUSTOMERS.Address = ADDRESSES.Address
ORDER BY ADDRESSES.SortOrder DESC;
2) Alternatively, if you can"t edit the database structure, using the IIf() function in the ORDER BY clause:
SELECT *
FROM CUSTOMERS
ORDER BY IIf([ADDRESS] = 'DELHI',1,IIf([ADDRESS] = 'BHOPAL',2,
IIf([ADDRESS] = 'KOTA',3,IIf([ADDRESS] = 'AHMADABAD',4,
IIf([ADDRESS] = 'MP',5,6))))) DESC;
You should avoid doing this unless you can't edit the database structure because function calls are slow, especially when multiple conditions are evaluated as in this example. If you need to specify any additional sorting for the "ADDRESS" field, you will need to add additional IIf() functions, which will slow this down even further.
If you're able to specify a sort order for every value of "ADDRESS", you should use a single Switch() function instead of nested IIf() functions.
CASE does not exist in Access. You can use Switch() instead.

Merge the values of two tables from different SQL Servers

I have two (linked) sql servers with basically the same setup but they differ in the content. What I want to do is take 2 tables (one from each server) and merge them, so that there are no duplicates ID (e.g., no duplicate fname) and that the the count are added together
They may look like
SERV1.DB1.dbo.Table:
| fname | count |
----------------------
| 'file1.txt' | 10 |
| 'file2.txt' | 5 |
| 'file3.txt' | 35 |
SERV2.DB2.dbo.Table:
| fname | count |
----------------------
| 'file1.txt' | 40 |
| 'file2.txt' | 150 |
And I want to write a select that outputs
| fname | count |
----------------------
| 'file1.txt' | 50 |
| 'file2.txt' | 155 |
| 'file3.txt' | 35 |
I don't want a join and a union doesn't merge them the way I want.
edit
It needs to be case-insensitive as the fname may (read: will) vary in case
fname has different collations (it's a no problem but worth mentioning)
I get these two tables by doing a similar select on each server. I could create temporary tables but I'd prefer if I didn't have too.
You can do it easly with a JOIN
SELECT table1.fname fname, table1.Count + table2.count Count
FROM SERV1.DB1.dbo.Table table1
FULL OUTER JOIN SERV2.DB2.dbo.Table table2 ON table1.fname=table2.fname
EDIT: case-sensitive/-insensitive depends on the column collation
This is my own recommendation, based on giammin's answer:
SELECT
COALESCE(t1.fname,t2.fname) fname,
COALESCE(t1.Count,0) + COALESCE(t2.count,0) Count
FROM
SERV1.DB1.dbo.Table t1
FULL OUTER JOIN
SERV2.DB2.dbo.Table t2
ON t1.fname=t2.fname
which ensures that all fname values from both tables appear in the result, even those that only appear in SERV2's table.