Im supposed to be converting this query from MYSQL to SQL Server. However, the join statement is throwing me off. I haven't seen joins done like this and I am slightly confused on how to translate it.
SELECT
`Supplier Confirmed Orders` + `Log Tech Confirmed Orders` AS 'orders confirmed',
`Orders in CVN`-`Cancelled Orders` AS 'Orders in CVN',
tblloadingmonths.`month`,
tblvendorindex.`vendorindexid`,
'Service' AS category
FROM
tblloadingmonths
JOIN
tblvendorindex
LEFT JOIN
tblcvn ON tblloadingmonths.`month` = tblcvn.`month`
AND tblvendorindex.vendorindexid = tblcvn.vendorindexid
Whats throwing me off is that the loadingmonths and vendorindex tables dont have any common fields, but theyre being joined, and then left joined with cvn. I've always been taught to do tableA join tableB ON colA = colB join tableC ON colB = colC, but not tableA join talbeB left join tableC ON colA = colC AND colB = colC. As it stands, the query cant run in SQL Server with the joins the way the way they are. I had to set it up like this:
SELECT
CVN.[Supplier Confirmed Orders] + CVN.[Log Tech Confirmed Orders] AS 'orders confirmed',
(CVN.[Orders in CVN] - CVN.[Cancelled Orders]) AS 'Orders in CVN',
tblloadingmonths.month,
tblvendorindex.vendorindexid,
'Service' AS category,
'CVN Compliance' as metric
FROM
cvn
JOIN
tblvendorindex ON tblvendorindex.vendorindexid = CVN.vendorindexid
INNER JOIN
tblloadingmonths ON tblloadingmonths.month = CVN.month
Im getting different results for this converted query. Any guidance would be greatly appreciated
You are taught correctly in that you should list your join clause (ON...) and the second query is obviously far more readable and is preferred. Similarly, old-style joins simply list everything in the WHERE clause for INNER JOIN but again, this is hard to read. Regarding your different results. They are different because
cvn table name is different from tblcvn meaning it's a different object. If that was a type-o then...
In the first you LEFT JOINing to the tblloadingmonths and tblloadingmonths tables... meaning the rows must exist in both of those (for your join clause) for tblcvn rows to be returned. However, since it's a LEFT JOIN, the rows for tblloadingmonths and tblloadingmonths will be returned regardless of the match in tblcvn. In the second, you are using cvn as the base table and using INNER JOIN throughout. This means that the match must exists for the join clause for the rows in cvn to be returned. Otherwise, they would be filtered.
To recap, when using LEFT JOIN...
The table in the FROM clause matters. Swapping it with a LEFT JOIN table could change the results (as you witnessed)
Criteria in a WHERE clause could turn the LEFT into an INNER join.
I can't tell you if MySQL automatically assigns a join based on key assignments, or if it's making a Cartesian product. When using JOIN in SQL Server you have to list the ON clause. You could use old style joins... FROM Table1, Table2... but this should get you as Cartesian without a WHERE clause. This should get you close with some edits on your part:
SELECT
CVN.[Supplier Confirmed Orders] + CVN.[Log Tech Confirmed Orders] AS 'orders confirmed',
(CVN.[Orders in CVN] - CVN.[Cancelled Orders]) AS 'Orders in CVN',
tblloadingmonths.month,
tblvendorindex.vendorindexid,
'Service' AS category,
'CVN Compliance' as metric
FROM
tblvendorindex
INNER JOIN
tblloadingmonths ON tblvendorindex.??? = tblloadingmonths.???? --find out what the relation is, a foreign key perpahs. Would need a data model to determine.
LEFT JOIN
cvn ON tblloadingmonths.month = cnv.month
AND tblvendorindex.vendorindexid = cvn.vendorindexid
Thanks to your comments, you confirmed the MySQL join was producing a Cartesian Product implicitly which you can achieve with a CROSS JOIN explicitly in SQL Server, as you answered.
SELECT
CVN.[Supplier Confirmed Orders] + CVN.[Log Tech Confirmed Orders] AS 'orders confirmed',
(CVN.[Orders in CVN] - CVN.[Cancelled Orders]) AS 'Orders in CVN',
tblloadingmonths.month,
tblvendorindex.vendorindexid,
'Service' AS category,
'CVN Compliance' as metric
FROM
tblvendorindex
CROSS JOIN tblloadingmonths
LEFT JOIN
cvn ON tblloadingmonths.month = cnv.month
AND tblvendorindex.vendorindexid = cvn.vendorindexid
Related
Use Cars and Rentals tables to retrieve records.
CARS(car_id, car_name, car_type)
RENTALS(rental_id, cust_id, car_id, pickup_date, km, fare)
SELECT c.car_id, c.car_name, c.car_type
FROM cars as c, rentals as r
WHERE c.car_id=r.car_id and r.pickup_date=null
ORDER BY c.car_id;
I've tried this but output is NO ROWS SELECTED
I would recommend NOT EXISTS for this query:
select c.*
from cars c
where not exists (select 1 from rentals r where r.car_id = c.car_id);
That said, your query has multiple errors:
The comma in the FROM clause is very last-century. Use JOIN.
= NULL is always going to filter out all rows. Almost all comparisons to NULL return NULL which is treated as "false". The correct comparison is IS NULL, but I don't think that is needed.
You can specify equivalent logic using LEFT JOIN, but I think NOT EXISTS is closer to the statement of the question.
Your original intent was to join the two tables and filters on rows that did not match on the right side of the join. This technique is sometimes called an anti left join.
Your attempt failed because you need a left join rather than an (implicit) inner join, and because you did not properly check for nullity (this requires operator is null).
The left join solution phrases as:
select c.*
from cars c
left join rentals r on r.car_id = c.car_id
where r.car_id is null
order by c.car_id
Note that I check for nullity on column car_id rather than pickup_date - any column that is not nullable can do, however I find the intent clearer when using the joining column.
A simple way to do this is with NOT IN:
SELECT car_id, car_name, car_type
FROM cars
WHERE car_id NOT IN (SELECT car_id FROM rentals)
ORDER BY car_id;
I have a query that looks like this:
select `adverts`.*
from `adverts`
inner join `advert_category` on `advert_category`.`advert_id` = `adverts`.`id`
inner join `advert_location` on `adverts`.`id` = `advert_location`.`advert_id`
where `advert_location`.`location_id` = ?
and `advert_category`.`category_id` = ?
order by `updated_at` desc
The problem here is I have a huge database and this response is absolutely ravaging my database.
What I really need is to do the first join, and then do there where clause. This will whittle down my response from like 100k queries to less than 10k, then I want to do the other join, in order to whittle down the responses again so I can get the advert_location on the category items.
Doing it as is just isn't viable.
So, how do I go about using a join and a where condition, and then after getting that response doing a further join with a where condition?
Thanks
This is your query, written a bit simpler so I can read it:
select a.*
from adverts a inner join
advert_category ac
on ac.advert_id = a.id inner join
advert_location al
on al.advert_id = a.id
where al.location_id = ? and
ac.category_id = ?
order by a.updated_at desc;
I am speculating that advert_category and advert_locations have multiple rows per advert. In that case, you are getting a Cartesian product for each advert.
A better way to write the query uses exists:
select a.*
from adverts a
where exists (select 1
from advert_location al
where al.advert_id = a.id and al.location_id = ?
) and
exists (select 1
from advert_category ac
where ac.advert_id = a.id and ac.category_id = ?
)
order by a.updated_at desc;
For this version, you want indexes on advert_location(advert_id, location_id), advert_category(advert_id, category_id), and probably advert(updated_at, id).
You can write the 1st join in a Derived Table including a WHERE-condition and then do the 2nd join (but a decent optimizer might resolve the Derived Table again and do what he thinks is best based on statistics):
select adverts.*
from
(
select `adverts`.*
from `adverts`
inner join `advert_category`
on `advert_category`.`advert_id` =`adverts`.`id`
where `advert_category`.`category_id` = ?
) as adverts
inner join `advert_location`
on `adverts`.`id` = `advert_location`.`advert_id`
where `advert_location`.`location_id` = ?
order by `updated_at` desc
MySQL will reorder inner joins for you during optimization, regardless of how you wrote them in your query. Inner join is the same in either direction (in algebra this is called commutative), so this is safe to do.
You can see the result of join reordering if you use EXPLAIN on your query.
If you don't like the order MySQL chose for your joins, you can override it with this kind of syntax:
from `adverts`
straight_join `advert_category` ...
https://dev.mysql.com/doc/refman/5.7/en/join.html says:
STRAIGHT_JOIN is similar to JOIN, except that the left table is always read before the right table. This can be used for those (few) cases for which the join optimizer processes the tables in a suboptimal order.
Once the optimizer has decided on the join order, it always does one join at a time, in that order. This is called the nested join method.
There isn't really any way to "do the join then do the where clause". Conditions are combined together when looking up rows for joined tables. But this is a good thing, because you can then create a compound index that helps match rows based on both join conditions and where conditions.
PS: When asking query optimization question, you should include the EXPLAIN output, and also run SHOW CREATE TABLE <tablename> for each table, and include the result. Then we don't have to guess at the columns and indexes in your table.
I am trying to emulate a full outer join in mySQL by using a left join and unioning it with a right join.
SELECT * FROM auxview_candiNotes AS a
LEFT JOIN auxview_clientNotes AS b ON a.dateAdded = b.dateAdded
UNION
SELECT * FROM auxview_candiNotes AS a
RIGHT JOIN auxview_clientNotes AS b ON a.dateAdded = b.dateAdded
The reason I am doing this is because I am joining two tables and occasionally one of them will not have a value on a specific date (thus the date is not listed in the table). However the joined table should display all dates, that occur in either or both of the tables. If one of the tables does not contain a value for a specific date, it should return NULL.
However, some dates from the auxview_clientNotes table are not listed in the joined table.
Thanks in advance!
This method isn't exactly a full outer join, because the union removes duplicates. Duplicates can be generated when the underlying tables have duplicates. Often duplicate removal is considered a "good" think, but the method is not exactly equivalent to a full outer join.
I prefer a left join approach:
select candi.*, client.*
from (select dateAdded from auxview_candiNotes union -- intentionally not `union all`
select dateAdded from auxview_clientNotes
) d left join
auxview_candiNotes candi
on candi.dateAdded = d.dateAdded left join
auxview_clientNotes client
on client.dateAdded = d.dateAdded;
The query I'm using calls on a few tables in the database and works fine. However, when I add line 10 to the mix it returns 50 or more repeated results. I'm still somewhat new to SQL and Sequel Pro so I'm sure the solution isn't too complicated but I am truly stumped right now.
Here is the code:
SELECT c.first_name, c.last_name, ca.company, ca.city, ca.state, ct.certificate_number, ct.certificate_date
FROM customer c, customer_type ctype, cust_address ca, certification ct, cust_prof_cert cp
WHERE ca.id_customer = c.id_customer LIKE cp.prof_cert_id_prof_cert
AND c.customer_type_id_customer_type = ctype.id_customer_type
AND ct.customer_id_customer = c.id_customer
AND ca.id_customer = c.id_customer
AND ctype.customer_type IN('CIRA','CIRA, CDBV')
AND ct.course_type_id_course_type = 1
AND ct.certificate_number IS NOT NULL
AND cp.prof_cert_id_prof_cert = "1"
ORDER BY ct.certificate_number ASC, c.last_name ASC;
Thank you for your time.
By Doing your SQL like that you are not relating the data, just selecting it. I would recommend changing your SQL to use JOINS.
SELECT Orders.OrderID, Customers.CustomerName, Orders.OrderDate
FROM Orders
INNER JOIN Customers
ON Orders.CustomerID=Customers.CustomerID;
Here is an article that might be able to help you a bit: w3schools, Joins
Here's your query using the SQL92 syntax for joins. You should use this syntax instead of the SQL89 "comma-style" joins.
SELECT c.first_name, c.last_name, ca.company, ca.city, ca.state,
ct.certificate_number, ct.certificate_date
FROM customer AS c
INNER JOIN customer_type AS ctype ON c.customer_type_id_customer_type = ctype.id_customer_type
INNER JOIN cust_address AS ca ON ca.id_customer = c.id_customer
INNER JOIN certification AS ct ON ct.customer_id_customer = c.id_customer
INNER JOIN cust_prof_cert AS cp -- what's this join condition?
WHERE ca.id_customer = c.id_customer LIKE cp.prof_cert_id_prof_cert
AND ctype.customer_type IN('CIRA','CIRA, CDBV')
AND ct.course_type_id_course_type = 1
AND ct.certificate_number IS NOT NULL
AND cp.prof_cert_id_prof_cert = '1'
ORDER BY ct.certificate_number ASC, c.last_name ASC;
A few weird things I notice in this query:
The first term in the WHERE clause is strange. You should know that LIKE has higher precedence than = so this might not be doing what you think it's doing. It's as if you wrote
WHERE ca.id_customer = (c.id_customer LIKE cp.prof_cert_id_prof_cert)
Which means evaluate the LIKE and produce a 0 or a 1 to represent the boolean condition. Then look for a ca.id_customer matching that 0 or 1.
Given that strange term, I can find no other join condition for the cp table. The default join if you give no restriction for it is that every row matches every row in the joined tables. So if you have 50 rows where cp.prof_cert_id_prof_cert = 1, then it will effectively multiply the results from the rest of the joined tables by 50.
This is called a Cartesian product, or in MySQL parlance it's counted in SHOW STATUS as a Full join.
ctype.customer_type IN('CIRA','CIRA, CDBV') You have quoted the second and third strings together. Basically, this means you are trying to match the column against two strings, one of which happens to contain a comma.
You probably meant to write ctype.customer_type IN('CIRA','CIRA','CDBV') so the column may match any of these three values.
I would suggest not querying multiple tables in your FROM clause, I believe this is the cause of your duplicate rows. If you separate out the tables into separate inner or left joins, (whichever you need) you should be able to match which ever keys in each table manually, instead of having SQL attempt to automatically do this.
I have 3 tables to select from. 2 of them are always necessary (tbl_notes, tbl_clients) while the 3rd is optional (tbl_notes_categories).
I've always used a LEFT JOIN in my queries with questionable correlating records to the primary table.
But I'm not getting any results with the query below.
Would someone point out how I'm using the LEFT JOIN incorrectly?
SELECT n.*, c.clientname, nc.notecategoryname
FROM tbl_notes n, tbl_clients c
LEFT JOIN tbl_notes_categories nc ON n.categoryid = nc.categoryid
WHERE n.clientid = c.clientid
AND c.clientid = 12345
ORDER BY n.dateinserted DESC
In fact, I'm getting a sql error.
#1054 - Unknown column 'n.categoryid' in 'on clause'
categoryid certainly does exist in tbl_notes
I probably need to brush up on how JOINS really work. I'm guessing I cannot have a LEFT JOIN with 2 database tables before it?
On a side note, I can foresee times when there will be multiple required tables, with several optional tables. (in this case tbl_notes_categories is optional)
Assuming the column categoryid exists in the tbl_notes table...
Try rewriting the query to use the JOIN syntax, rather than using the old-school comma as the join operator. (If the problem isn't a misnamed column, I suspect the problem is in mixing the two types of syntax... but this is just a suspicion, I have no reason to test mixing old-style comma joins with JOIN keywords.)
I'd write the statement like this:
SELECT n.*, c.clientname, nc.notecategoryname
FROM tbl_notes n
JOIN tbl_clients c
ON n.clientid = c.clientid
LEFT
JOIN tbl_notes_categories nc
ON nc.categoryid = n.categoryid
WHERE c.clientid = 12345
ORDER BY n.dateinserted DESC
(Actually, I would specify the individual columns to return from n, rather than using n.*, but that's just a style preference, not a SQL syntax requirement.)