Get result from multiple result-JOIN or Union? - join

I am a newbie on the database side.
I have three tables. as per the below screen.
I want to create a view that shows records from job_document and engineer_certficate tables
job document holds, manually uploaded document info and enginnner_certficate holds document data filled out by the android application.
On UI, I want to show available documents from both tables. I tried with "join" but did not get the expected result.
SELECT * FROM document_type d join job_document jd ON jd.document_type_id = d.id JOIN `engineer_certficate` ec on ec.document_type_id = d.id ;
I have also tries Union, but still no luck
Expected Result
Show all columns of job_document and engineer_certficate
CREATE TABLE `document_type` ( `id` int(5) NOT NULL, `name` varchar(55) NOT NULL, `description` varchar(255) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin1; INSERT INTO `document_type` (`id`, `name`, `description`) VALUES (1, 'A', ''),(2, 'B', ''),(3, 'C', ''),(4, 'D', ''); CREATE TABLE `engineer_certficate` ( `id` int(10) NOT NULL, `job_id` int(10) NOT NULL, `document_type_id` mediumint(4) NOT NULL, `certficate_info` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL CHECK (json_valid(`certficate_info`))) ENGINE=InnoDB DEFAULT CHARSET=latin1; INSERT INTO `engineer_certficate` (`id`, `job_id`, `document_type_id`, `certficate_info`) VALUES(1, 1, 2, '{\"test\":\"abc\"}'),(2, 2, 2, '{\"test\":\"abc\"}'); CREATE TABLE `job_document` ( `id` int(10) NOT NULL, `job_id` int(10) NOT NULL, `document_type_id` mediumint(4) NOT NULL, `document_label` varchar(55) NOT NULL, `document_path` varchar(500) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin1; INSERT INTO `job_document` (`id`, `job_id`, `document_type_id`, `document_label`, `document_path`) VALUES(1, 1, 1, 'DocA', '2022/02/doca.pdf'),(2, 1, 3, 'DocC', '2022/02/docc.pdf');
ALTER TABLE `document_type` ADD PRIMARY KEY (`id`); ALTER TABLE `engineer_certficate` ADD PRIMARY KEY (`id`); ALTER TABLE `job_document` ADD PRIMARY KEY (`id`);

Related

Doctrine hydrates relations wrong, but result set after applying SQL directly is right

Basically, I'm trying to select all Users with their Permissions filtered by CurrentCompany, so if we switched CurrentCompany we should have only Users with their Permissions only for that CurrentCompany.
For testing purposes, I have added two Companies, two Users (f.i. A and B), and assigned user A to both Companies, and user B just only to one Company. Also, user A has different permissions set for each company, and user B has permissions just only for one company he added in.
While querying directly MySQL with generated SQL shows the right resultset, but the resulting Collection for User::permissions() has all permissions for all companies User assigned to, but there should be just only permissions related to the CurrentCompany.
Hope I described it well to understand.
So, in two words, the issue is that hydrated User->permissions() relation have all permissions assigned for the User for all companies he assigned to, but querying SQL directly shows the right results.
QueryBuilder + resulting DQL/SQL:
$this->createQueryBuilder('u')
->join('u.companies', 'c', Join::WITH, 'c.company = :company')
->leftJoin('u.permissions', 'p', Join::WITH, 'p.company = :company')
// ->andWhere('c.company = :company')
// ->andWhere('p.company = :company')
->setParameter('company', $this->tenant->company()->id()->toBinary())
->getQuery()
->getResult()
);
DQL:
SELECT u FROM App\Identity\Domain\User\User u INNER JOIN u.companies c WITH c.company = :company LEFT JOIN u.permissions p WITH p.company = :company
SQL:
SELECT u0_.id AS id_0, u0_.email AS email_1, u0_.username AS username_2, u0_.password AS password_3, u0_.created AS created_4, u0_.deleted AS deleted_5, u0_.active_company_id AS active_company_id_6 FROM user u0_ INNER JOIN user_company u1_ ON u0_.id = u1_.user_id AND (u1_.company_id = 0x3DF17103A3E14FD09D1CEF98D8318230) LEFT JOIN user_permission u2_ ON u0_.id = u2_.user_id AND (u2_.company_id = 0x3DF17103A3E14FD09D1CEF98D8318230) WHERE ((u0_.deleted IS NULL OR u0_.deleted > CURRENT_TIMESTAMP));
DB structure:
CREATE TABLE `user` (
`id` binary(16) NOT NULL,
`email` varchar(180) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`deleted` datetime DEFAULT NULL,
`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
`active_company_id` binary(16) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UNIQ_8D93D649E7927C74` (`email`),
KEY `FK_USER_ACTIVE_COMPANY` (`active_company_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `user_company` (
`user_id` binary(16) NOT NULL,
`company_id` binary(16) NOT NULL,
`email` varchar(180) COLLATE utf8mb4_unicode_ci NOT NULL,
`id` binary(16) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `UNIQ_USER_COMPANY` (`user_id`,`company_id`),
KEY `FK_17B21745979B1AD6` (`company_id`),
CONSTRAINT `FK_17B21745979B1AD6` FOREIGN KEY (`company_id`) REFERENCES `Company` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `user_permission` (
`user_id` binary(16) NOT NULL,
`permission_id` binary(16) NOT NULL,
`id` binary(16) NOT NULL,
`company_id` binary(16) NOT NULL,
PRIMARY KEY (`id`),
KEY `FK_USER_PERMISSION` (`user_id`),
KEY `FK_USER_PERMISSION_PERMISSION` (`permission_id`),
KEY `FK_USER_PERMISSION_COMPANY` (`company_id`),
CONSTRAINT `FK_USER_PERMISSION_COMPANY` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`) ON DELETE CASCADE,
CONSTRAINT `FK_USER_PERMISSION_PERMISSION` FOREIGN KEY (`permission_id`) REFERENCES `permission` (`id`) ON DELETE CASCADE,
CONSTRAINT `FK_USER_PERMISSION_USER` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
Doctrine mapping:
App\Identity\Domain\User\User:
type: entity
table: user
...
oneToMany:
permissions:
targetEntity: App\Identity\Domain\User\UserPermission
orphanRemoval: true
mappedBy: user
joinTable:
name: user_permission
cascade: [ "all" ]
companies:
targetEntity: App\Identity\Domain\UserCompany\UserCompany
mappedBy: user
joinTable:
name: user_company
cascade: [ "all" ]
App\Identity\Domain\User\UserPermission:
type: entity
table: user_permission
...
manyToOne:
user:
targetEntity: App\Identity\Domain\User\User
inversedBy: permissions
joinTable:
name: user_permission
App\Identity\Domain\UserCompany\UserCompany:
type: entity
table: user_company
...
manyToOne:
company:
targetEntity: App\Identity\Domain\Company\Company
inversedBy: userCompanies
user:
targetEntity: App\Identity\Domain\User\User
inversedBy: companies
Okay, the solution is to use Query::HINT_REFRESH or $em->clear(), because I found there was another query for the User entity in middleware, and Doctrine uses cached results from IdentityMap, if the entity is already present there.

Oracle database where clause

I have the following db with tables and and i do try to query them:
Retrieve the names, addresses, and number of books checked out for all borrowers who have more than five books checked out.
I have created the query
select name, Address
from BORROWER
where BOOK_LOANS.BookId >= 5;
Seems i need a join for the tables, i'm stuck here, Can I please get help?
CREATE TABLE BOOK (
BookId CHAR(20) NOT NULL,
Title VARCHAR(30) NOT NULL,
PublisherName VARCHAR(20),
PRIMARY KEY (BookId),
FOREIGN KEY (PublisherName) REFERENCES PUBLISHER (PublisherName)
);
CREATE TABLE BOOK_AUTHORS (
BookId CHAR(20) NOT NULL,
AuthorName VARCHAR(30) NOT NULL,
PRIMARY KEY (BookId, AuthorName),
FOREIGN KEY (BookId) REFERENCES BOOK (BookId)
ON DELETE CASCADE
);
// PublisherName was added, as it was referenced by another
// table but did not exist as an attribute.
CREATE TABLE PUBLISHER (
PublisherName VARCHAR(20) NOT NULL,
Address VARCHAR(40) NOT NULL,
Phone CHAR(12),
PRIMARY KEY (PublisherName)
);
CREATE TABLE BOOK_COPIES (
BookId CHAR(20) NOT NULL,
BranchId INTEGER NOT NULL,
No_Of_Copies INTEGER NOT NULL,
PRIMARY KEY (BookId, BranchId),
FOREIGN KEY (BookId) REFERENCES BOOK (BookId)
ON DELETE CASCADE,
FOREIGN KEY (BranchId) REFERENCES BRANCH (BranchId)
ON DELETE CASCADE
);
CREATE TABLE BORROWER (
CardNo INTEGER NOT NULL,
Name VARCHAR(30) NOT NULL,
Address VARCHAR(40) NOT NULL,
Phone CHAR(12),
PRIMARY KEY (CardNo)
);
CREATE TABLE BOOK_LOANS (
CardNo INTEGER NOT NULL,
BookId CHAR(20) NOT NULL,
BranchId INTEGER NOT NULL,
DateOut DATE NOT NULL,
DueDate DATE NOT NULL,
PRIMARY KEY (CardNo, BookId, BranchId),
FOREIGN KEY (CardNo) REFERENCES BORROWER (CardNo)
ON DELETE CASCADE,
FOREIGN KEY (BranchId) REFERENCES BRANCH (BranchId)
ON DELETE CASCADE,
FOREIGN KEY (BookId) REFERENCES BOOK (BookId)
ON DELETE SET NULL
);
CREATE TABLE BRANCH (
BranchId INTEGER NOT NULL,
BranchName VARCHAR(20) NOT NULL,
Address VARCHAR(40) NOT NULL,
PRIMARY KEY (BranchId)
);
You do indeed need a join that connects the BORROWER table with the BOOK_LOANS table. The join should be on the shared key (which is CardNO).
To count the number of books borrowed you use the COUNT() aggregate function, and you combine that with a GROUP BY clause that partition your data in to groups based on the unique combination of CardNo, Name, Address.
Now you have a count of books borrowed for each CardNo.
Finally you apply the HAVING clause to filter the groups to only include those groups that have a count of five or more books borrowed.
SELECT b.Name, b.Address, COUNT(*) AS BookCount
FROM BORROWER b
JOIN BOOK_LOANS bl ON b.CardNo = bl.CardNo
GROUP BY b.CardNO, b.Name, b.Address
HAVING COUNT(bl.BookID) >= 5

Rails: efficient way to collect data of one model from another with many-to-many relationship

I have two models, Bookmark and Tag. The tags are implemented by acts-as-taggable-on gem, but I will explain the main point here.
Bookmark model contains an url field. Given a Bookmark instance #bookmark, #bookmart.tags returns its tags. Different bookmarks can share a same tag (that is the many part from Tag).
Tag has name, and taggings_count. The taggings_count field stores how many bookmarks to which the tag are tagged. Behind the scene, there is an taggings table, but that doesn't matter.
Now is the question, I want to retrieve all those tags that are tagged by bookmarks with specific url value, and the result should be sorted by the number of bookmarks to which a certain tag is tagged (that number is not the same as the taggings_count field, which represents tagging count for all bookmark, but want bookmark for a specific url here). How can it be done so that the generated sql is efficient?
I know I can write directly in sql for efficiency, but I am also wondering whether Rails can do the same without hurting too much performance, so that I don't have to inject sql code in my Rails application
Following is the table definitions, in the taggings table, taggable_id acts as a foreign key to Bookmark, and tag_id a foreign key to Tag:
CREATE TABLE `bookmarks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) DEFAULT NULL,
`url` varchar(255) DEFAULT NULL,
`description` text,
`private` tinyint(1) DEFAULT NULL,
`read` tinyint(1) DEFAULT NULL,
`list_id` int(11) DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
`updated_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
CREATE TABLE `taggings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`tag_id` int(11) DEFAULT NULL,
`taggable_id` int(11) DEFAULT NULL,
`taggable_type` varchar(255) DEFAULT NULL,
`tagger_id` int(11) DEFAULT NULL,
`tagger_type` varchar(255) DEFAULT NULL,
`context` varchar(128) DEFAULT NULL,
`created_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `taggings_idx` (`tag_id`,`taggable_id`,`taggable_type`,`context`,`tagger_id`,`tagger_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
CREATE TABLE `tags` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`taggings_count` int(11) DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `index_tags_on_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
This is a solution:
bookmark_ids = Bookmark.where(url: "http://foobar.com").pluck(:id)
taggings = Tagging.where(taggable_id: bookmark_ids).where(taggable_type: "Bookmark")
tag_ids = taggings.pluck(:tag_id).uniq
tags = Tag.where(id: tag_ids).order(taggings_count: :desc)
It theory it could be written using the joins() method from ActiveRecord, but I don't know how the gem you're using defines the associations.
This might or might not work:
Tag.order(taggings_count: :desc)
.joins(taggings: :bookmark)
.where(bookmark: { url: "http://foobar.com" })
You could also write raw SQL, but it feels dirty in rails.
It turns out to be simple, acts-as-taggable-on can actually operates on an association. The docs have that sample, I just didn't noticed:
Bookmark.where(url: url).tag_counts_on(:tags).sort_by(&:taggings_count).reverse.map(&:name)
The only problem is that, the result is stilled sorted by popularity of those tags, no matter in what kinds of context they are popular. That doesn't really matter, though. The point is this rails statement only generates a single sql:
SELECT tags.*, taggings.tags_count AS count FROM "tags" JOIN (SELECT taggings.tag_id, COUNT(taggings.tag_id) AS tags_count FROM "taggings" INNER JOIN bookmarks ON bookmarks.id = taggings.taggable_id WHERE (taggings.taggable_type = 'Bookmark' AND taggings.context = 'tags') AND (taggings.taggable_id IN(SELECT bookmarks.id FROM "bookmarks" WHERE "bookmarks"."url" = 'http://kindleren.com/forum.php?gid=49')) GROUP BY taggings.tag_id HAVING COUNT(taggings.tag_id) > 0) AS taggings ON taggings.tag_id = tags.id

Rails 3 ActiveRecord inserting autoincrement field issue

I'm using:
Rails 3.0.9
Activerecord-sqlserver-adapter 3.0.15
TinyTds
MSSQL 2005
I have following table:
CREATE TABLE [dbo].[eclaims](
[id_app] [int] IDENTITY(1,1) NOT NULL,
[id_user] [int] NOT NULL,
[property] [varchar](4) NOT NULL,
[app_number] [varchar](15) NULL,
[patent_number] [varchar](15) NULL,
[native_number] [varchar](20) NULL,
[title] [nvarchar](max) NULL,
[applicants] [nvarchar](max) NULL,
[receive_date] [datetime] NULL,
[change_date] [datetime] NOT NULL CONSTRAINT [DF_eclaims_change_date] DEFAULT (getdate()),
CONSTRAINT [PK_eclaims] PRIMARY KEY CLUSTERED
(
[id_app] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[eclaims] WITH CHECK ADD CONSTRAINT [FK_eclaims_users] FOREIGN KEY([id_user])
REFERENCES [dbo].[users] ([id])
GO
ALTER TABLE [dbo].[eclaims] CHECK CONSTRAINT [FK_eclaims_users]
The model is:
# coding: utf-8
class Eclaim < ActiveRecord::Base
end
As you can see there is auto-increment field named id_app.
When I try execute a query insert into eclaims (id_user, [property], title) values (1, 2, 'Alex') in MSSQL console everything goes perfect.
But when I try Eclaim.create(:id_user => 1, :property => 'inv', :change_date => Time.now )
I am getting such error TinyTds::Error: DEFAULT or NULL are not allowed as explicit identity values.: INSERT INTO [eclaims] ([id_app], [id_user], [property], [app_number], [patent_number], [native_number], [title], [applicants], [receive_date], [change_date]) VALUES (NULL, 1, N'inv', NULL, NULL, NULL, NULL, NULL, NULL, '2012-05-08 06:39:14.882')
Why ActiverRecord doesn't insert the auto-increment field id_app automatically?
Thanks in advance.
Rails is all conventions and it assumes 'id' column to be the primary key. However, since you chose to set 'id_app' to be your primary key of the table, you should tell that to Rails as well.
Use set_primary_key(value = nil, &block) to set 'id_app' as the primary key and it should work.

rails - boolean operators in find

params[:codes] = "9,10"
#result = Candidate.find :all,
:joins =>
params[:codes].split(',').collect {|c| ["INNER JOIN candidates_codes on candidates_codes.candidate_id = candidates.id, INNER JOIN codes on codes.code_id = candidates_codes.code_id AND codes.value = ?", c]}
Error
Association named 'INNER JOIN candidates_codes on candidates_codes.candidate_id = candidates.id, INNER JOIN codes on codes.code_id = candidates_codes.code_id AND codes.value = ?' was not found; perhaps you misspelled it?
Update
CREATE TABLE `candidates` (
 `id` int(11) NOT NULL auto_increment,
`first_name` varchar(255) collate utf8_unicode_ci default NULL,
`last_name` varchar(255) collate utf8_unicode_ci default NULL,
`mobile_number` varchar(255) collate utf8_unicode_ci default NULL,
`address` text collate utf8_unicode_ci,
`country` varchar(255) collate utf8_unicode_ci default NULL,
`created_at` datetime default NULL,
`updated_at` datetime default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=5 ;
CREATE TABLE `candidates_codes` (
`candidate_id` int(11) default NULL,
`code_id` int(11) default NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `codes` (
`id` int(11) NOT NULL auto_increment,
`section` varchar(255) collate utf8_unicode_ci default NULL,
`value` varchar(255) collate utf8_unicode_ci default NULL,
`created_at` datetime default NULL,
`updated_at` datetime default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=11 ;
Hi,
I am trying to create a find which can ether be "OR" or "AND"
for example(pseudocode)
array a = (1,2)
array b = (1)
find(1 AND 2) = array a
find(1 OR 2) = array a, array b
My code currently looks like this -
#result = Code.all :joins => :candidates,
:conditions => ["codes.id IN (?)", params['searches'][:Company]],
:select => "candidates.*"
code is a table full of codes that describe a candidate,
a habtm relationship exists between code and candidate
The only way of using AND I can see in the guides is between two columns..
Many Thanks
Alex
Since the association is done with a join table, doing an AND requires an INNER JOIN, once for each term in the AND. What you're trying to do is find a given candidate that has a mapping for all of the codes.
This could get messy, since you not only have to join for each term, but also again to the codes table if you're matching on a field there, such as value.
Assuming the number of terms isn't too high, and you pass in params[:codes] = "1,5,9", and that you're trying to match on codes.value:
Candidate.find :all,
:joins =>
params[:codes].split(',').collect {|c| "INNER JOIN candidates_codes#{c} on candidates_codes#{c}.candidate_id = candidates.id INNER JOIN codes#{c} on codes#{c}.id = candidates_codes#{c}.code_id AND codes#{c}.value = c"}
...or something like that. Warning that I haven't tested that code, but give it a whirl if that's what you're looking for.
Note I've removed the substitution from the last rev (where the ? is replaced by a variable) because joins don't support this. You should first sanitize the params (i.e. make sure they are integers, or whatever), or use the protected sanitize_sql method in the model.

Resources