now i am use spring data jpa to develpoe.
no i have a problem.table relation is blow:
table a has table b's id, table b has table c's id.
CREATE TABLE testa
(
a_id UUID NOT NULL,
name CHARACTER VARYING(256),
b_id UUID,
PRIMARY KEY (a_id)
);
INSERT INTO testa (a_id, name, b_id) VALUES ('1673da5f-20a2-4959-82ff-d92db4e951d2','table a', '37c78baf-6b10-42b4-961d-da570c396050');
CREATE TABLE testb
(
b_id UUID NOT NULL,
name CHARACTER VARYING(256),
c_id UUID,
PRIMARY KEY (b_id)
);
INSERT INTO testb (b_id, name, c_id) VALUES ('37c78baf-6b10-42b4-961d-da570c396050','table b', '50169479-cf16-412a-9a6d-3bf19a111a0b');
CREATE TABLE testc
(
c_id UUID NOT NULL,
name CHARACTER VARYING(256),
PRIMARY KEY (c_id)
);
INSERT INTO testc (c_id, name) VALUES ('50169479-cf16-412a-9a6d-3bf19a111a0b','table c');
i want to implement:
select
*
from
testa a
left join
testb b
on
b.b_id = a.b_id
left join
testc c
on
c.c_id = b.c_id
where c.name = 'table c';
but in spring jpa.
#Override
public Predicate toPredicate(Root<RecoveredResource> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Join<TESTA, TESTB> join = root.join("b", JoinType.LEFT);
Join<TESTB, TESTC> join2 = join.join("c", JoinType.LEFT);
this code doesn't work. it has error.
i don't know how to do this in spring data jpa
Related
This – allegedly easy – task currently I cannot solve.
SQL Fiddle
http://sqlfiddle.com/#!17/90dce/1
Schema
Given this schema and data
CREATE TABLE asset (
"id" BIGINT NULL DEFAULT NULL,
"name" TEXT NULL DEFAULT NULL,
PRIMARY KEY ("id")
);
CREATE INDEX IF NOT EXISTS "IDX_id" ON asset (id);
CREATE TABLE category (
"id" BIGINT NULL DEFAULT NULL,
"ctype" TEXT NULL DEFAULT NULL,
"name" TEXT NULL DEFAULT NULL,
PRIMARY KEY ("id")
);
CREATE INDEX IF NOT EXISTS "IDX_id" ON category (id);
CREATE TABLE asset_category (
"asset_id" BIGINT NULL DEFAULT NULL,
"category_id" BIGINT NULL DEFAULT NULL,
CONSTRAINT "FK_asset_id" FOREIGN KEY ("asset_id") REFERENCES "asset" ("id") ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT "FK_category_id" FOREIGN KEY ("category_id") REFERENCES "category" ("id") ON UPDATE CASCADE ON DELETE SET NULL,
UNIQUE (asset_id, category_id)
);
INSERT INTO asset (id, "name") VALUES(1, 'Awesome Asset with a hit');
INSERT INTO asset (id, "name") VALUES(2, 'Great Asset without a hit');
INSERT INTO category (id, "name", "ctype") VALUES(1, 'First Category', NULL);
INSERT INTO category (id, "name", "ctype") VALUES(2, 'Second Category', 'directory');
INSERT INTO asset_category ("asset_id", "category_id") VALUES(1, 1);
INSERT INTO asset_category ("asset_id", "category_id") VALUES(1, 2);
INSERT INTO asset_category ("asset_id", "category_id") VALUES(2, 1);
Task
I want to get all assets with their category Id (in case they have one of type "directory". Otherwise NULL as category.
See my query below, I wrote two joins letting me limit the results in the ON clause. However, since both are related to the other category, the first JOIN hinders me to get a clean result.
What I tried
This query Query A
SELECT a.id "assetId", c.id "categoryId"
FROM asset a
LEFT JOIN asset_category ac ON ac.asset_id = a.id
left join category c on (
c.id = ac.category_id
AND
c.ctype = 'directory'
)
restulting in:
assetId categoryId
1 (null)
1 2
2 (null)
That is almost good, except, assetId 1 appears twice. This probably due to first JOIN, which creates a relation to assetcategory and the other category not of type 'directory'. Same as assetId 2.
Query B uses inner join:
SELECT a.id "assetId", c.id "categoryId"
FROM asset a
LEFT JOIN asset_category ac ON ac.asset_id = a.id
inner join category c on (
c.id = ac.category_id
AND
c.ctype = 'directory'
)
resulting in
assetId categoryId
1 2
However, here the problem is, it hides asset with id 2 for me as join is not successfully resolving asset id 2.
Desired output
assetId | categoryId
1 | 2
2 | null
I would be really happy about this seemingly simple task.
demo:db<>fiddle
Your first query is a good approach. It seems you wanted only one record per id. This is what is DISTINCT ON for:
SELECT DISTINCT ON (a.id)
a.id, c.id
FROM asset a
LEFT JOIN asset_category ac ON a.id = ac.asset_id
LEFT JOIN category c ON c.id = ac.category_id AND c."ctype" = 'directory'
ORDER BY a.id, ctype NULLS LAST
So, just order your joined result by id first, and order ctype = NULL records to bottom, which makes the directory values bubble up being the first one. DISTINCT ON takes the first record for each id afterwards which is the one you expect.
so if i have a table
CREATE TABLE customers(
customer_id INT GENERATED ALWAYS AS IDENTITY,
customer_name VARCHAR(255) NOT NULL,
PRIMARY KEY(customer_id)
);
CREATE TABLE contacts(
contact_id INT GENERATED ALWAYS AS IDENTITY,
customer_id INT,
contact_name VARCHAR(255) NOT NULL,
phone VARCHAR(15),
email VARCHAR(100),
PRIMARY KEY(contact_id),
CONSTRAINT fk_customer
FOREIGN KEY(customer_id)
REFERENCES customers(customer_id)
);
So i wanted to check all the tables that is referencing to customers.
Something like this :
TABLE_NAME|COLUMN_NAME|TABLE_REFERENCES|COLUMN_REFERENCES
contacts|customer_id|customers|customer_id
So it would basically tells me that in the table contacts with field customer_id, it is a foreign key reference to table customers with field customer_id.
Is there a way to do this?
the solution you're looking for is detailed in this blog post
Basically you'll need to parse the information_schema tables table_constraints, key_column_usage, referential_constraints, table_constraints.
The following query should be a good starting point
select kcu.table_schema || '.' ||kcu.table_name as foreign_table,
rel_tco.table_schema || '.' || rel_tco.table_name as primary_table,
kcupk.column_name as pk_column,
kcu.column_name as fk_column,
kcu.constraint_name
from information_schema.table_constraints tco
join information_schema.key_column_usage kcu
on tco.constraint_schema = kcu.constraint_schema
and tco.constraint_name = kcu.constraint_name
join information_schema.referential_constraints rco
on tco.constraint_schema = rco.constraint_schema
and tco.constraint_name = rco.constraint_name
join information_schema.table_constraints rel_tco
on rco.unique_constraint_schema = rel_tco.constraint_schema
and rco.unique_constraint_name = rel_tco.constraint_name
join information_schema.key_column_usage kcupk
on rel_tco.constraint_schema = kcupk.constraint_schema
and rel_tco.constraint_name = kcupk.constraint_name
where tco.constraint_type = 'FOREIGN KEY'
order by kcu.table_schema,
kcu.table_name;
I need to migrate data from an old Books table:
create table dbo.Books_OLD (
Id int identity not null constraint PK_Books_OLD_Id primary key (Id),
Title nvarchar (200) not null,
Image varbinary (max) null,
Preview varbinary (max) null
)
To a new table structure:
create table dbo.Books (
Id int identity not null constraint PK_Books_Id primary key (Id),
Title nvarchar (200) not null
)
create table dbo.Files (
Id int identity not null constraint PK_Files_Id primary key (Id),
Content varbinary (max) null,
Name nvarchar (280) null
)
create table dbo.BookFiles (
BookId int not null,
FileId int not null,
constraint PK_BookFiles_Id primary key (BookId, FileId)
)
alter table dbo.BookFiles
add constraint FK_BookFiles_BookId foreign key (BookId) references Books(Id) on delete cascade on update cascade,
constraint FK_BookFiles_FileId foreign key (FileId) references Files(Id) on delete cascade on update cascade;
The migration should run as follows:
Books_OLD.Title => Create new Book with given Title value
Books_OLD.Image => Create new File with Image content.
Create new BookFile to associate File to Book.
Books_OLD.Preview => Create new File with Preview content.
Create new BookFile to associate File to Book.
I was able to migrate the data but somehow when I run this:
select FileId
from BookFiles
group by FileId
having count(*) > 1;
I have duplicates. I should not have duplicate FileIds. What am I missing?
The migration code I have is:
DECLARE #BOOKS table (
BookId int,
Image varbinary(max),
Preview varbinary(max)
)
MERGE Books AS d
USING Books_OLD AS s
ON 0 = 1
WHEN NOT MATCHED
THEN INSERT (Title)
VALUES (s.Title)
OUTPUT INSERTED.Id, s.Image, s.Preview
INTO #BOOKS;
INSERT Files (Content, Created)
SELECT t.Content, GETUTCDATE()
FROM #BOOKS i
CROSS APPLY (VALUES (Preview, 'Preview'), (Image, 'Image')) t(Content, ContentType)
WHERE Content IS NOT NULL
INSERT BookFiles (BookId, FileId)
SELECT i.BookId, f.Id
FROM #BOOKS i
JOIN Files f
ON f.Content = i.Image
UNION ALL
SELECT i.BookId, f.Id
FROM #BOOKS i
JOIN Files f
ON f.Content = i.Preview
Some Books can have two files (Image and Preview) so BookId can appear more than once in BooksFiles.
But each file (Image or Preview) in Books_OLD table should only be associated with one Book. So it is strange that I have duplicated FileId in BookFiles.
What am I missing?
If you have the same image or preview for different book in your Books_Old, in your original code from this part:
INSERT BookFiles (BookId, FileId)
SELECT i.BookId, f.Id
FROM #BOOKS i
JOIN Files f
ON f.Content = i.Image
It will return you more results when doing the INNER JOIN because two image or preview from different books can be joined. And the duplicate FileId is actually a bad record, because the BookId is not correspond to that particular Image or Preview even though they are the same.
What you could do is have another table variable called #Files, similar to the Files table structure, you just need to add one more column, which is BookId, then:
INSERT BookFiles (BookId, FileId)
SELECT i.BookId, f.Id
FROM #BOOKS i
JOIN #Files f
ON f.Content = i.Image
AND f.BookId = i.BookId --added joining condition
--assume code before has inserted bookId into `#Files`
So at last, you pick all the needed columns from #Files, insert them to Files.
UPDATE: please refer below for the full codes:
DECLARE #BOOKS table (
BookId int,
Image varbinary(max),
Preview varbinary(max)
)
--Added #File Variable
DECLARE #Files table
(
BookId int,
Content varbinary (max) null,
Created nvarchar (280) null,
Id int identity(1,1) not null primary key
)
MERGE Books AS d
USING Books_OLD AS s
ON 0 = 1
WHEN NOT MATCHED
THEN INSERT (Title)
VALUES (s.Title)
OUTPUT INSERTED.Id, s.Image, s.Preview
INTO #BOOKS;
INSERT #Files (BookId,Content, Created) --
SELECT i.BookId,t.Content, GETUTCDATE()
FROM #BOOKS i
CROSS APPLY (VALUES (Preview, 'Preview'), (Image, 'Image')) t(Content, ContentType)
WHERE Content IS NOT NULL
INSERT BookFiles (BookId, FileId)
SELECT i.BookId, f.Id
FROM #BOOKS i
JOIN #Files f
ON f.Content = i.[Image]
AND f.BookId = i.BookId --added joining condition
UNION ALL
SELECT i.BookId, f.Id
FROM #BOOKS i
JOIN #Files f
ON f.Content = i.Preview
AND f.BookId = i.BookId --added joining condition
--Last insert all needed from #File into File
INSERT INTO Files (Content, Created)
SELECT content,Created
FROM #Files
PS: Not sure whether there is a typo for dbo.File, you have Name in your table definition, but when inserting, its Created
I need to copy content from one table to itself and related tables... Let me schematize the problem. Let's say I have two tables:
Order
OrderID : int
CustomerID : int
OrderName : nvarchar(32)
OrderItem
OrderItemID : int
OrderID : int
Quantity : int
With the PK being autoincremental.
Let's say I want to duplicate the content of one customer to another. How do I do that efficiently?
The problem are the PKs. I would need to map the values of OrderIDs from the original set of data to the copy in order to create proper references in OrderItem. If I just select-Insert, I won't be able to create that map.
Suggestions?
For duplicating one parent and many children with identities as the keys, I think the OUTPUT clause can make things pretty clean (SqlFiddle here):
-- Make a duplicate of parent 1, including children
-- Setup some test data
create table Parents (
ID int not null primary key identity
, Col1 varchar(10) not null
, Col2 varchar(10) not null
)
insert into Parents (Col1, Col2) select 'A', 'B'
insert into Parents (Col1, Col2) select 'C', 'D'
insert into Parents (Col1, Col2) select 'E', 'F'
create table Children (
ID int not null primary key identity
, ParentID int not null references Parents (ID)
, Col1 varchar(10) not null
, Col2 varchar(10) not null
)
insert into Children (ParentID, Col1, Col2) select 1, 'g', 'h'
insert into Children (ParentID, Col1, Col2) select 1, 'i', 'j'
insert into Children (ParentID, Col1, Col2) select 2, 'k', 'l'
insert into Children (ParentID, Col1, Col2) select 3, 'm', 'n'
-- Get one parent to copy
declare #oldID int = 1
-- Create a place to store new ParentID
declare #newID table (
ID int not null primary key
)
-- Create new parent
insert into Parents (Col1, Col2)
output inserted.ID into #newID -- Capturing the new ParentID
select Col1, Col2
from Parents
where ID = #oldID -- Only one parent
-- Create new children using the new ParentID
insert into Children (ParentID, Col1, Col2)
select n.ID, c.Col1, c.Col2
from Children c
cross join #newID n
where c.ParentID = #oldID -- Only one parent
-- Show some output
select * from Parents
select * from Children
Do you have to have the primary keys from table A as primaries in Table B? If not you can do a select statement with an insert into. Primary Key's are usually int's that start from an ever increasing seed (identity). Going around this and declaring an insert of this same data problematically has the disadvantage of someone thinking this is a distinct key set on this table and not a 'relationship' or foreign key value.
You can Select Primary Key's for inserts into other tables, just not themselves.... UNLESS you set the 'identity insert on' hint. Do not do this unless you know what this does as you can create more problems than it's worth if you don't understand the ramifications.
I would just do the ole:
insert into TableB
select *
from TableA
where (criteria)
Simple example (This assumes SQL Server 2008 or higher). My bad I did not see you did not list TSQL framework. Not sure if this will run on Oracle or MySql.
declare #Order Table ( OrderID int identity primary key, person varchar(8));
insert into #Order values ('Brett'),('John'),('Peter');
declare #OrderItem Table (orderItemID int identity primary key, OrderID int, OrderInfo varchar(16));
insert into #OrderItem
select
OrderID -- I can insert a primary key just fine
, person + 'Stuff'
from #Order
select *
from #Order
Select *
from #OrderItem
Add an extra helper column to Order called OldOrderID
Copy all the Order's from the #OldCustomerID to the #NewCustomerID
Copy all of the OrderItems using the OldOrderID column to help make the relation
Remove the extra helper column from Order
ALTER TABLE Order ADD OldOrderID INT NULL
INSERT INTO Order (CustomerID, OrderName, OldOrderID)
SELECT #NewCustomerID, OrderName, OrderID
FROM Order
WHERE CustomerID = #OldCustomerID
INSERT INTO OrderItem (OrderID, Quantity)
SELECT o.OrderID, i.Quantity
FROM Order o INNER JOIN OrderItem i ON o.OldOrderID = i.OrderID
WHERE o.CustomerID = #NewCustomerID
UPDATE Order SET OldOrderID = null WHERE OldOrderID IS NOT NULL
ALTER TABLE Order DROP COLUMN OldOrderID
IF the OrderName is unique per customer, you could simply do:
INSERT INTO [Order] ([CustomerID], [OrderName])
SELECT
2 AS [CustomerID],
[OrderName]
FROM [Order]
WHERE [CustomerID] = 1
INSERT INTO [OrderItem] ([OrderID], [Quantity])
SELECT
[o2].[OrderID],
[oi1].[Quantity]
FROM [OrderItem] [oi1]
INNER JOIN [Order] [o1] ON [oi1].[OrderID] = [o1].[OrderID]
INNER JOIN [Order] [o2] ON [o1].[OrderName] = [o2].[OrderName]
WHERE [o1].[CustomerID] = 1 AND [o2].[CustomerID] = 2
Otherwise, you will have to use a temporary table or alter the existing Order table like #LastCoder suggested.
I have a simple employee table that I want to display in a particular order. I want to find out if there are alternative solutions (or better solution) to achieve the same result. The T-SQL script is shown below:
CREATE TABLE Employee(
EmployeeID INT IDENTITY(1,1) NOT NULL,
EmployeeName VARCHAR(255) NULL,
ManagerID INT NULL,
EmployeeType VARCHAR(20) NULL
)
GO
INSERT INTO Employee (EmployeeName, ManagerID, EmployeeType)
VALUES ('Brad',5,'Memeber');
INSERT INTO Employee (EmployeeName, ManagerID, EmployeeType)
VALUES ('James',3,'Memeber');
INSERT INTO Employee (EmployeeName, ManagerID, EmployeeType)
VALUES ('Ray',null,'Manager');
INSERT INTO Employee (EmployeeName, ManagerID, EmployeeType)
VALUES ('Tom',8,'Memeber');
INSERT INTO Employee (EmployeeName, ManagerID, EmployeeType)
VALUES ('Neil',8,'Memeber');
INSERT INTO Employee (EmployeeName, ManagerID, EmployeeType)
VALUES ('Rob',5,'Memeber');
INSERT INTO Employee (EmployeeName, ManagerID, EmployeeType)
VALUES ('Paul',5,'Memeber');
INSERT INTO Employee (EmployeeName, ManagerID, EmployeeType)
VALUES ('Tim',null,'Manager');
GO
SELECT e.EmployeeType, e.EmployeeName AS [Team Member],
(SELECT e2.EmployeeName FROM Employee AS e2 WHERE e2.EmployeeID = e.ManagerID) AS Manager
FROM Employee AS e
ORDER BY e.EmployeeType, e.EmployeeID
The rows are ordered by manger first, then employeeID. My concerns is that in my solution, it is sorted by the EmployeeType column. Would it be better to sort it by ManagerId column instead? Because the EmployeeType could be changed in the future, say from Manager to Team Manager, which might cause different result!
If the criteria for a manager is that column ManangerID is null, you can use a case in the order by to get the managers first.
SELECT e.EmployeeType, e.EmployeeName AS [Team Member],
(SELECT e2.EmployeeName FROM Employee AS e2 WHERE e2.EmployeeID = e.ManagerID) AS Manager
FROM Employee AS e
ORDER BY CASE WHEN E.ManagerID IS NULL THEN 0 ELSE 1 END, e.EmployeeID
If you want to set the sort depending on EmployeeType you can do like this
SELECT e.EmployeeType, e.EmployeeName AS [Team Member],
(SELECT e2.EmployeeName FROM Employee AS e2 WHERE e2.EmployeeID = e.ManagerID) AS Manager
FROM Employee AS e
ORDER BY
CASE EmployeeType
WHEN 'Manager' THEN 0
WHEN 'Memeber' THEN 1
ELSE 2
END, e.EmployeeID
Or you can use a table with EmpType's that define the sort order
CREATE TABLE EmpType(EmployeeType VARCHAR(20) PRIMARY KEY, SortOrder INT)
GO
INSERT INTO EmpType VALUES('Manager', 1)
INSERT INTO EmpType VALUES('Memeber', 2)
SELECT e.EmployeeType, e.EmployeeName AS [Team Member],
(SELECT e2.EmployeeName FROM Employee AS e2 WHERE e2.EmployeeID = e.ManagerID) AS Manager
FROM Employee AS e
LEFT OUTER JOIN EmpType as et
ON e.EmployeeType = et.EmployeeType
ORDER BY et.SortOrder, e.EmployeeID
There are no "universal" solution. In your example, not only Employee type but Manager_id can change too.
If you need to get similar results, you should order to hierarchy level. In this case first will be manager set, then employee. If Employee will have another managerId, it will stay at the same level.