I have two tables:
Books
id
publisher_id
Publishers
id
name
Books is associated with Publishers in the BooksTable class like so:
public function initialize(array $config){
$this->belongsTo('Publishers');
}
I want to display a list of books along with their publishers, here is my code:
$query = $this->Books->find()
->contain('Publishers')
->select(['id', 'title', 'Publishers.name']);
$this->paginate($query);
This works fine, however I noticed that it produces the following queries:
SELECT * FROM books Books
LEFT JOIN publishers Publishers ON Publishers.id = (Books.publisher_id)
ORDER BY Books.id desc LIMIT 20 OFFSET 0
SELECT (COUNT(*)) AS `count` FROM books Books
LEFT JOIN publishers Publishers ON Publishers.id = (Books.publisher_id)
The Pagination component automatically strips out the unneeded code such as GROUP, ORDER, etc. from the second count query but it keeps the LEFT JOIN.
Is there a reason why this isn't removed and is there a way to tell it to ignore certain associations when querying the row count?
Say for example following is data for your table.
Books
id | publisher_id
-----------------
1 | 1
2 | 1
Publishers
id | name
----------
1 | publisher1
1 | publisher2
LEFT JOIN output
book_id | publisher_id | name
-----------------------
1 | 1 | publisher1
1 | 1 | publisher2
2 | 1 | publisher1
2 | 1 | publisher2
I know, For your case publisher_id is unique. but that's not case all time. Now you can see that LEFT JOIN has row count 4 while only 2 book there.
Related
I tried searching this kind of use case on google but did found exact what i was looking so, Need help to reach to specific point by useful answer, docs or any reference
I have this situation: table A has to be joined with table B and table C; B and C have similar columns, so there will be duplicate column names in select part, however give preferences to all the data of tables B if available otherwise show data from table c
For example:
SELECT
ae.*, ml.name as name, ml.contact AS contact,
ve.name AS name, ve.contact AS contact
FROM
TABLE ae
LEFT JOIN
TABLE ml ON ae.eid = ml.eid
LEFT JOIN
TABLE ve ON ae.eid = ve.eid
WHERE
ae.eid = 1
ml data
eid | name | contact
----+------+--------
1 | xyz | null
ve data
eid | name | contact
----+------+--------
1 | xyz | 1
ae data
eid | gender
----+--------
1 | male
I want this result:
eid | gender | name | contact
----+--------+------+--------
1 | male | xyz | null
But I am getting this for now:
eid | gender | name | contact | contact
----+--------+------+---------+--------
1 | male | xyz | 1 | null
I'm using node-mssql driver for querying SQL Server data.
Thanks,
You must join ve only if there is no matching row in ml and you do it if you add in the ON clause the condition ... AND ml.eid IS NULL.
Also use COALESCE() to select the columns from ml first and if they don't exist from ve:
SELECT ae.*,
COALESCE(ml.name, ve.name) AS name,
COALESCE(ml.contact, ve.contact) AS contact
FROM ae
LEFT JOIN ml ON ae.eid = ml.eid
LEFT JOIN ve ON ae.eid = ve.eid AND ml.eid IS NULL
WHERE ae.eid = 1
See the demo.
Results:
eid
gender
name
contact
1
male
xyz
null
Let's say I have this table
(Simplified, my query have more tables involved)
ConsignmentItem
ID |Item code| Name | Quantity
1 | 00000 | A | 3
2 | 11111 | B | 2
And this other table
PickingItem
ID |ConsignmentID|Quantity
1 | 1 | 1
What my query does is to join both tables and print the amount of products ordered and the amount of products already registered. I would like to get as result the following table
Item Code| Name | Quantity_Ordered | Quantity_Registered
00000 | A | 3 | 1
11111 | B | 2 | 0
My query works whenever the item exist on "PickingItem", if it doesn't it prints the same "Quantity_Registered" as the above row, using my query I get as result the following table
Item Code| Name | Quantity_Ordered | Quantity_Registered
00000 | A | 3 | 1
11111 | B | 2 | 1(this is wrong)
This is the query i'm using
SELECT C.Barcode AS 'Item Code',C.ProductName AS 'Name', C.Quantity AS 'Quantity_Ordered', ISNULL(P.Quantity,0) AS 'Quantity_Registered'
FROM PICKING.OrderPickingItem P
JOIN PICKING.OrderPicking OP ON P.PickingID = OP.PickingID
JOIN ORDERS.ConsignmentItem C ON OP.ConsignmentID = C.ConsignmentID
WHERE P.PickingID = 1 --For testing
Anyone know what could I do to, if the product doesn't exist on OrderPickingItem, then set P.Quantity = 0 for that specific row?
EDIT:
Structure of the tables
OrderPickingItem
PickingItemID PK
PickingID FK
ConsignmentItemID FK
Quantity
--other not used columns for this query
OrderPicking
PickingID PK
ConsignmentID FK
--other not used columns for this query
ConsignmentItem
ConsignmentItemID PK
ConsignmentID FK
Barcode
Quantity
ProductName
--other not used columns for this query
You are obviously looking for an outer join:, you want to show ConsignmentItem records even when there is no matching picking.
select
C.Barcode AS "Item Code",
C.ProductName AS "Name",
C.Quantity AS "Quantity_Ordered",
ISNULL(P.Quantity, 0) AS "Quantity_Registered"
from ORDERS.ConsignmentItem c
left join PICKING.OrderPicking op on OP.ConsignmentID = C.ConsignmentID
left join PICKING.OrderPickingItem P on P.PickingID = OP.PickingID
and P.ConsignmentItemID = C.ConsignmentItemID;
Do an outer join and something like ifnull(p.id, 0) for the quantity in the select clause.
I have a new database schema that I need to query and I am having trouble getting the data I need without using a primary key in the JOIN. I didn't design the schema but I have to work with it. I tried creating a SQLFiddle for this but it kept giving me a gateway error when I tried.
Basically I have a Contacts table that contains ALL the contacts stored in the database. The Contacts table contains two fields that reference the tables that the contact is linked to (Vendor and Client). The Contact table also contains a field named "ContactType". This links to another table that defines the type of Contact (Owner or Employee).
[Client]
ClientID | Name
==============================
1 | Uptown Dining
2 | Downtown Eatery
3 | Midtown Steakhouse
[Vendor]
VendorID | Name
==============================
1 | ABC Produce
2 | DEF Seafood
3 | GHI Meats
[ContactType]
ContactTypeID | Name
==============================
1 | Owner
2 | Employee
[Contact]
ContactID | TableName | TableID | ContactTypeID | FirstName | LastName
========================================================================
1 | Client | 1 | 1 | Bob | Smith
2 | Vendor | 1 | 1 | Jill | Weston
3 | Vendor | 1 | 2 | Alice | Jenkins
4 | Client | 2 | 1 | Chris | Brown
5 | Vendor | 3 | 2 | Todd | Davis
What I am trying to do is get a list of Client contacts who are the owners of their company. This SQl works to do just that.
SELECT
Contact.FirstName,
Contact.LastName
FROM Client
LEFT JOIN Contact
ON Client.ClientID = Contact.TableID
AND Contact.TableName = 'Client'
AND Contact.ContactTypeID = 1
This would give me ..
Bob Smith
Chris Brown
The problem is I don't want to use AND Contact.ContactTypeID = 1 in the JOIN. I want to be able to use Owner or Employee in place of the primary key (1 or 2) of the ContactType table in the JOIN but I am unsure of how to do this. Please keep in mind the restriction needs to be in the JOIN since I need to reserve the WHERE clause to filter specific clients.
For example what if I wanted to query for a specific Vendor and get a list of the owner and employee in the same row ...
Vendor Name | Owner First | Owner Last | Employee First | Employee Last
============================================================================
ABC Produce | Jill | Weston | Alice | Jenkins
I'm not sure I understand your problem correctly, but if I'm reading it right - you want to have the chance to specify "Owner" or "Employee" explicitly in your WHERE clause rather than 1 or 2.
Perhaps this is what you are looking for:
SELECT
Contact.FirstName,
Contact.LastName
FROM Client
LEFT JOIN Contact
ON Client.ClientID = Contact.TableID
AND Contact.TableName = 'Client'
LEFT JOIN ContactType ct ON ct.ContactTypeID = Contact.ContactTypeID
-- Now you can use it directly, as below
WHERE ct.Name = 'Owner'
Edit: Response to your comment
Not without getting messy... You can use a derived inline table, or a view, but all of these would be performance killers. The least performance-impairing (but still ugly) way I can think of is something like this:
SELECT
Contact.FirstName,
Contact.LastName
FROM Client
LEFT JOIN Contact
ON Client.ClientID = Contact.TableID
AND Contact.TableName = 'Client'
AND Contact.ContactTypeID = (SELECT iix.ContactTypeID FROM ContactType iix WHERE iix.Name = 'Owner')
Im building a website that has questionnaire that users fill out. Currently my db looks something like below.
Website Questionnaire Consists of
25 Questions
4 to 6 Answers per questions the user can choose from.
Issue
I want to add in the users country, state / province / city.
I need to incorporate this into my search function. See sql statement below.
The client provided me a list of 23 countries to store and 750 states/provinces and about 6000 cities.
Were should this go in my db? Im completely lost on this one?
Current DB Design
See fiddle http://sqlfiddle.com/#!6/bf068/1
User_Table
ID | UserName
0 | Jack
...
User Questionnaire_Questions_Answer
ID | user_id | question_id | answer_id
0 | 0 | 0 | 0
1 | 0 | 1 | 3
...
Questionnaire_Questions
ID | Question
0 | What type of music do you like?
1 | What is your favorite sport ?
...
Questionnaire_Answers
ID | Answer
0 | Rock
1 | Rap
2 | Basketball
3 | Soccer
...
SQL STATEMENT FOR SEARCH
Searches best questionnaire results based on what the preferences the user is looking for, Sorts on Count on highest totalmatches
SELECT
User_Table.id,
User_Table.UserName,
COUNT(User_Table.id) as totalMatches
FROM User_Table
INNER JOIN Questionnaire_Questions_Answer ON User_Table.id = Questionnaire_Questions_Answer.user_ID
INNER JOIN Questionnaire_Questions ON Questionnaire_Questions.id = Questionnaire_Questions_Answer.question_ID
INNER JOIN Questionnaire_Answers on Questionnaire_Answers.id = Questionnaire_Questions_Answer.answer_ID
WHERE
--Q and A Requested to Match
Questionnaire_Questions.id = '0' and Questionnaire_Answers.answer = '0'
OR
Questionnaire_Questions.id = '1' and Questionnaire_Answers.answer = '3'
GROUP BY User_Table.id
ORDER BY totalMatches DESC
Example Results
ID | Name | totalMatches
0 | Jack | 2
create a location table as so:
create table dbo.location (id int identity,
country varchar(200),
state_province varchar(100),
city varchar 100))
when you add a user add the id of the location to the user table.
Consider the following tables:
Applications
+----+-------------+---------+----------+
| id | previous_id | next_id | status |
+----+-------------+---------+----------+
| 1 | NULL | 3 | Archived |
+----+-------------+---------+----------+
| 3 | 1 | 4 | Archived |
+----+-------------+---------+----------+
| 4 | 3 | NULL | Approved |
+----+-------------+---------+----------+
Comments
+-----+--------+----------------+
| id | app_id | comment |
+-----+--------+----------------+
| 1 | 1 | Testing |
+-----+--------+----------------+
| 2 | 1 | Still testing |
+-----+--------+----------------+
| 3 | 4 | No longer |
+-----+--------+----------------+
The Applications table contains revisions of an application. The comments table contains comments that were left on each iteration of the application. They are tied together via:
Applications.ID = Comments.APP_ID
There is a breadcrumb that will tell you if there were any previous revisions to the application, and if you are dealing with the current one or not (the current row will always have NULL as the next_id value.
I want to build two things.
A count of how many revisions an application has had. It may be zero revisions, or it may be 16+.
A list of comments for any given current application (like ID = 4) and all previous applications, ordered newest comment to oldest comment.
Using a recursive CTE, you can build a list of revisions for any given application. The following query will give you a list of revision/comments, ordered by revision (newest first), and comment id (newest first), separated by base application. There is an extra count column revision_count duplicated for each row, so this is all done in one query. Note the revision count is 0-based; that is, the count is the number of revisions to the base, not including the base. I believe this is what you want. The result column revision is the 1-based revision number (1 is the oldest, base revision) in the sequence.
You can of course extrapolate or adjust as necessary.
;WITH Base AS
(
SELECT id, next_id, status, 1 AS revision
FROM Applications
WHERE previous_id IS NULL
), Revisions AS
(
SELECT id, next_id, status, revision, id AS BASEID
FROM Base
UNION ALL
SELECT a.id, a.next_id, a.status, r.revision + 1 AS revision, r.BASEID
FROM Applications a
INNER JOIN Revisions r ON a.id = r.next_id
), RevisionCounts AS
(
SELECT COUNT(1) - 1 AS revision_count, BASEID
FROM Revisions
GROUP BY BASEID
)
SELECT r.BASEID, r.revision, r.id, r.status, c.comment, rc.revision_count
FROM Revisions r
LEFT OUTER JOIN Comments c ON c.app_id = r.id
INNER JOIN RevisionCounts rc ON r.BASEID = rc.BASEID
ORDER BY r.BASEID, r.revision DESC, c.id DESC
And here is a fiddle to go along with it, demonstrating the query in action. The returned data is:
BASEID REVISION ID STATUS COMMENT REVISION_COUNT
1 3 4 Approved No longer 2
1 2 3 Archived (null) 2
1 1 1 Archived Still testing 2
1 1 1 Archived Testing 2
select
ID, count(previous_id)
from
applications
where
previous_id is not null group by id ---record is not new
UNION
Select
ID,0
from
applications
where
previous_id is null--when the record is new and does not have any revisions
select
a.id, comments
from
applications a join comments c on a.id=c.id
order by
C.id