Mapping table MySQL / Access - mysql

I have a short access/mySQL question. I have a mapping table on the format below.
ID Category_A Category_B Category_C Team
1 a b T1
2 a d T2
I have a second table which also includes Category_A, Category_B, and Category_C. I would like to join the Team value to the my second table based on the mappingtable. My problem is that when there is a blank (e.g. ID=2, Category_B) the mapping should assign the T2 to any row that contains Category_A=a and Category_C=d regardless of the value in Category_B.
Can this type of mapping be done?
Grateful for your help!

In MS Access, I think you would need something on the lines of:
SELECT t.ID, m.Team
FROM Team t
INNER JOIN Mapping m
ON (m.Category_C = t.Category_C)
AND (m.Category_B = t.Category_B)
AND (m.Category_A = t.Category_A)
WHERE m.Category_C Is Not Null
AND m.Category_B Is Not Null
AND m.Category_A Is Not Null
UNION ALL
SELECT t.ID, m.Team
FROM Team t
INNER JOIN Mapping m
ON (m.Category_B = t.Category_B)
AND (m.Category_A = t.Category_A)
WHERE m.Category_C Is Null
AND m.Category_B Is Not Null
AND m.Category_A Is Not Null
UNION ALL
SELECT t.ID, m.Team
FROM Team t
INNER JOIN Mapping m
ON (m.Category_C = t.Category_C)
AND (m.Category_A = t.Category_A)
WHERE m.Category_C Is Not Null
AND m.Category_B Is Null
AND m.Category_A Is Not Null
UNION ALL
SELECT t.ID, m.Team
FROM Team t
INNER JOIN Mapping m
ON (m.Category_C = t.Category_C)
AND (m.Category_B = t.Category_B)
WHERE m.Category_C Is Not Null
AND m.Category_B Is Not Null
AND m.Category_A Is Null

Related

repeated rows in json_agg() in query with 2 lateral joins

I have a strange result when performing a lateral join on a query
I have the following table structure
task->id
comment -> id , taskId, comment
tasklink -> taskId, type, userid
with a single task record (id 10), 1 comment record ("row1", "a test comment") and 5 tasklink records (all with taskid 10)
I expected this query
select task.id,
json_agg(json_build_object('id',c.id, 'user',c.comment)) as comments,
json_agg(json_build_object('type',b.type, 'user',b.userid)) as users
FROM task
left join lateral (select c.* from comment c where task.id = c.taskid) c on true
left join lateral (select b.* from taskuserlink b where task.id = b.taskid) b on true
where task.id = 10
GROUP BY task.id ;
to return
id | comments | users
---------------------------------------------------------------------
10 "[{"id":"row1","user":"a test comment"}]" "[{"type":"updatedBy","user":1},"type":"closedBy","user":5},"type":"updatedBy","user":5},"type":"createdBy","user":5},{"type":"ownedBy","user":5}]"
instead, I got this
id | comments | users
10 "[{"id":"row1","user":"a test comment"},{"id":"row1","user":"a test comment"},{"id":"row1","user":"a test comment"},{"id":"row1","user":"a test comment"},{"id":"row1","user":"a test comment"}]" "[{"type":"updatedBy","user":1},{"type":"closedBy","user":5},{"type":"updatedBy","user":5},{"type":"createdBy","user":5},{"type":"ownedBy","user":5}]"
ie , for every link row, the comment row is duplicated
I am thinking that I am missing something really obvious, but as I have only just started using Postgres (and sql ) I'm a little stumped
I would appreciate some guidance on where I'm going wrong
Move the aggregates into subqueries:
select id, comments, users
from task t
left join lateral (
select json_agg(json_build_object('id',c.id, 'user',c.comment)) as comments
from comment c
where t.id = c.taskid
) c on true
left join lateral (
select json_agg(json_build_object('type',b.type, 'user',b.userid)) as users
from taskuserlink b
where t.id = b.taskid
) b on true
DbFiddle.

How to get the union of 2 fields values from 2 tables in one field?

Am looking for the help of some sql query experts.
Having some hard time for me to fix the issue in below sql statement. This is a join sql statement of 3 tables. and i need similar kind of data from people and alumni table. And the field name also same in both table. So my question is- is there any for me to get the similar data in a single field?
SELECT DISTINCT P.people_id, P.Name,P.Journal_name, N.People_id, N.Name, N.Journal_name
FROM `Paper_Author` AS A
LEFT JOIN `People` AS P ON ( A.Author_id = P.people_id )
LEFT JOIN `Alumni` AS N ON ( A.Author_id = N.People_id )
WHERE A.Paper_id =2067
ORDER BY A.Author_sortorder
LIMIT 0 , 30
Eg of Current Result is:
Id-- Name-- Journal_name-- ID2-- Name-- Journal_name
1 Name1 A1 NULL NULL NULL
2 Name2 B1 5 Name10 NULL
3 Name3 C1 3 Name3 C1
Expected Result :
Id-- Name-- Journal_name--
1 Name1 A1
2 Name2 B1
3 Name3 C1
5 Name10 NULL
I want to know whether i can get similar values in single filed? eg:both Journal_name in single field?
A UNION should work for this task. With a UNION statement you'll get both results in a single result set:
SELECT DISTINCT P.people_id, P.Name, P.Journal_name, AP.Author_sortorder
FROM `Paper_Author` AS AP
LEFT JOIN `People` AS P ON ( A.Author_id = P.people_id )
WHERE AP.Paper_id = 2067
UNION
SELECT DISTINCT N.People_id, N.Name, N.Journal_name, AN.Author_sortorder
FROM `Paper_Author` AS AN
LEFT JOIN `Alumni` AS N ON ( A.Author_id = N.People_id )
WHERE AN.Paper_id = 2067
ORDER BY Author_sortorder

SQL query to get children of entity into same row as parent entity

I have a table that has the following fields:
| entity_id | parent_entity_id | name | status |
|----------------------------------------------|
I'm attempting to write a query that displays every entity that doesn't have a parent and displays their children's name and status inline for a result like this:
| entity_id | name | child_entity_1_name | child_entity_1_status |...| child_entity_4_name | child_entity_4_status |
--------------------------------------------------------------------------------------------------------------------
I know the data is structured so that every entity has at least 3 children, but not every entity has 4 (therefore the ones with 3 will have NULL in the columns for the 4th child name and status). Furthermore, I know that no entity that has a parent is a parent itself.
From the introductory database classes I've taken, this seems like a complicated query. The part that's tripping me up is getting all of the sub-entities into the same row. I can get one sub-entity in the same row as its parent but can't get more than one.
EDIT: The database is basically a set of trees each with a height of 2. There are no grandparents.
PARENT_ENT_1 PARENT_ENT_2
| | | | | | | |
| | | | | | | |
C1 C2 C3 C4 C5 C6 C7 C8
Every row in my result query should represent one of these trees
This works: http://sqlfiddle.com/#!9/e1127f/27/0
But I feel like it should be much, much easier.
I basically had this:
SELECT P.entity_id as Parent_id, P.name as Parent_Name, C1.entity_id, C1.Name,
C2.entity_id, C2.Name, C3.entity_id, C4.Name, C4.entity_id, C4.Name
FROM entity P
JOIN entity C1 on C1.parent_entity_id = P.entity_id
JOIN entity C2 on C2.parent_entity_id = P.entity_id
JOIN entity C3 on C3.parent_entity_id = P.entity_id
LEFT JOIN entity C4 on C4.parent_entity_id = P.entity_id
WHERE P.parent_entity_id IS NULL
AND C1.entity_id < C2.entity_id
AND C2.entity_id < C3.entity_id
AND C3.entity_id < C4.entity_id
But of course that final join won't work as it is there, because the WHERE clause turns it into an INNER join.. Maybe someone will see an easy way to handle that part.
I ended up relenting and using a UNION, one half for parents with 3 children and the other for parents with 4.
Edit: Thank you Paul for making the final join work!
SELECT P.entity_id as Parent_id, P.name as Parent_Name, C1.entity_id, c1.Name,
C2.entity_id, c2.Name, C3.entity_id, c3.Name, C4.entity_id, c4.Name
FROM entity P
JOIN entity C1 on C1.parent_entity_id = P.entity_id
JOIN entity C2 on C2.parent_entity_id = P.entity_id
JOIN entity C3 on C3.parent_entity_id = P.entity_id
LEFT JOIN entity C4 on C4.parent_entity_id = P.entity_id
and c3.entity_id < c4.entity_id
WHERE p.parent_entity_id IS NULL
AND C1.entity_id < C2.entity_id
AND C2.entity_id < C3.entity_id
AND (3 = (SELECT COUNT(1)
FROM entity c
WHERE c.parent_entity_id = p.entity_id)
OR c4.entity_id is not null)
Here is a query for two children:
select
p.entity_id, p.name,
c1.name as child_entity_1_name,
c1.status as child_entity_1_status,
c2.name as child_entity_2_name,
c2.status as child_entity_2_status
from entities p
left join entities c1 on c1.entity_id = (
select c.entity_id
from entities c
where c.parent_entity_id = p.entity_id
order by c.entity_id asc
limit 1
offset 0
)
left join entities c2 on c2.entity_id = (
select c.entity_id
from entities c
where c.parent_entity_id = p.entity_id
order by c.entity_id asc
limit 1
offset 1
)
where p.parent_entity_id is null
For child_entity_3 you will use offset 2 and for child_entity_4 you will use offset 3.
But I would rather just use the following two queries
select p.entity_id, p.name
from entities p
where p.parent_entity_id is null;
select p.entity_id as parent_id, c.name, c.status
from entities p
join entities c on c.parent_entity_id = p.entity_id
where p.parent_entity_id is null
order by p.entity_id, c.entity_id;
and create the desired table in application language with a couple of simple loops.
SET #cNum := 0;
SET #prevParent := 0;
SELECT p.id, p.Name
, GROUP_CONCAT(IF(numberedChildren.childNum = 1, c.Name, NULL)) AS child_entity_1_name
, GROUP_CONCAT(IF(numberedChildren.childNum = 1, c.Status, NULL)) AS child_entity_1_status
, GROUP_CONCAT(IF(numberedChildren.childNum = 2, c.Name, NULL)) AS child_entity_2_name
, GROUP_CONCAT(IF(numberedChildren.childNum = 2, c.Status, NULL)) AS child_entity_2_status
, ...
FROM (
SELECT #cNum := IF(#prevParent <> orderedChildren.parent_id, #cNum + 1, 1) AS childNum
, orderedChildren.id AS child_id
, #prevParent := orderedChildren.parent_id AS parent_id
FROM (
SELECT parent_id, id
FROM sometable
ORDER BY parent_id, id
) AS orderedChildren
) AS numberedChildren
INNER JOIN sometable AS p ON numberedChildren.parent_id = p.id
INNER JOIN sometable AS c ON numberedChildren.child_id = c.id
GROUP BY p.id, p.Name
;
I think this script might work. It relies on GROUP_CONCAT, and pretty much any other aggregate function, ignoring null values.
You can probably also make it a single query (dropping the initial SET statements) by changing this line:
) AS orderedChildren
to
) AS orderedChildren, (SELECT #cNum AS cnInit, #prevParent AS ppInit) As init
but that is not my usual style for session variable init.
Edit: Also, ordered children may not NEED to be a subquery (you might be able to do the ORDER BY and childNum calculation in the same subquery) but such use of session variables can be...delicate.
This is a bit messy, and there's probably a better means of storing this, especially because this is only manually scalable.
Assuming a table:
CREATE TABLE
parents
(
entity_id INT PRIMARY KEY AUTO_INCREMENT,
parent_entity_id INT,
name VARCHAR(15),
`status` VARCHAR(15)
);
EDITED
With some sample data:
INSERT INTO
`parents`
(entity_id, parent_entity_id, name, `status`)
VALUES
(1, NULL, 'Parent1', 'sfsd'),
(2, 1, 'Child1A', 'sfsd'),
(3, 1, 'Child1B', 'sfsd'),
(4, 1, 'Child1C', 'sfsd'),
(5, NULL, 'Parent2', 'sfsd'),
(6, 5, 'Child2A', 'sfsd'),
(7, 5, 'Child2B', 'sfsd');
You can create a view, temporary table or permanent table (depending on your ultimate goal) that stores the following:
SET #row_number = 0;
SET #parent_id = 0;
SELECT
#row_number:=CASE
WHEN #parent_id = parent_entity_id THEN #row_number + 1
ELSE 1
END AS `child_num`,
entity_id,
#parent_id:= parent_entity_id as parent_entity_id,
name,
`status`
FROM
`parents`
WHERE
`parent_entity_id` IS NOT NULL
ORDER BY
parent_entity_id ASC,
entity_id ASC;
The above would be easier with SQL Server and using PARTITION BY and ROW_NUMBER, but this is a way around it.
Gives us:
Then, you could join that table/view 3 times, adding a second JOIN condition for the child number. This is demoed here, using a derived table due to the restrictions in SQL Fiddle with data modification, which I supposed could be done all 3 times, though you'd have to look into the efficiency and benchmarking.
http://sqlfiddle.com/#!9/5ddef3/3
Ultimately, it gives us:

Whats wrong with this query? i am getting null result

SELECT `acart`.`order_number` AS `admin_order_number`,
`acart`.`user_id` AS `admin_user_id`,
`acart`.`created_by` AS `admin_created_by`,
`rcart`.`order_number` AS `renew_order_number`,
`rcart`.`user_id` AS `renew_user_id`,
`rcart`.`created_by` AS `renew_created_by`,
`scart`.`order_number` AS `shopping_order_number`,
`scart`.`user_id` AS `shopping_user_id`,
`scart`.`created_by` AS `shopping_created_by`
FROM `cdp_order_transaction_master` AS `master`
LEFT JOIN `cdp_admin_shopping_cart` AS `acart`
ON `acart`.`order_number`=`master`.`order_number`
LEFT JOIN `cdp_renew_cart` AS `rcart`
ON `rcart`.`order_number`=`master`.`order_number`
LEFT JOIN `cdp_shopping_cart` AS `scart`
ON `scart`.`order_number`=`master`.`order_number`
WHERE master.order_number IS NULL
Let me explain my problem,if the order is successfull then it will goes to cdp_order_transaction_master table and any other 3 table (cdp_admin_shopping_cart,cdp_renew_cart,cdp_shopping_cart) depending on the situation but if the order fails then it will not go to cdp_order_transaction_master table and remain in other tables,
so i want the failed order which is not present in cdp_order_transaction_master and can present in any other tables(cdp_admin_shopping_cart,cdp_renew_cart,cdp_shopping_c‌​art)
WHERE master.order_number IS NULL
This is your primary table, and the join condition for all other tables are on this column.
You are trying to join null to null
Based on your comment, try:
select 'cdp_admin_shopping_cart' as `err_table`, a1.order_number
from cdp_admin_shopping_cart a1
where not exists (select 1 from cdp_order_transaction_master a2 where a2.order_number = a1.order_number)
union all
select 'cdp_renew_cart' as `err_table`, a1.order_number
from cdp_renew_cart a1
where not exists (select 1 from cdp_order_transaction_master a2 where a2.order_number = a1.order_number)
union all
select 'cdp_shopping_c‌​art' as `err_table`, a1.order_number
from cdp_shopping_c‌​art a1
where not exists (select 1 from cdp_order_transaction_master a2 where a2.order_number = a1.order_number)

SQL , get difference b/w to id's

I want to get result from two tables:
Table A
create table A
( propertyId int not null
,PRIMARY KEY (propertyId))
Table B
create table B
( Id int not null
, propertyId int
, FOREIGN KEY (propertyId ) REFERENCES A(propertyId))
Now I want to get the result total count id of Table A exist in Table B AND total count id that does not exist in Table B
SELECT COUNT(property.propertyId) AS 'Occupied'
,(
SELECT COUNT(property.propertyId)
FROM property
INNER JOIN agreement ON property.`propertyId` <> agreement.`propertyId`
) AS 'Vacant'
FROM property
INNER JOIN agreement ON property.`propertyId` = agreement.`propertyId`
WHERE agreement.`isActive` = '1'
You can do a Left join to get the proper result.
select
sum (
case when a.propertyId is not null then 1
else 0
end
) as present_cnt,
sum (
case when a.propertyId is null then 1
else 0
end
) as not_present_cnt
from property p
left join agreement a on p.propertyId = a.propertyId
where a.isActive = '1';
left join will fetch data for a.propertyId from agreement corresponding to p.propertyId, if no match found then null
Your solution should be based on this principle, also exemplified in this SQLFiddle:
SELECT count(CASE
WHEN propertyID IS NOT NULL
AND fk_propertyID IS NOT NULL
THEN 1
ELSE NULL
END) 'exist_in_both_tables'
,count(CASE
WHEN propertyID IS NOT NULL
AND fk_propertyID IS NULL
THEN 1
ELSE NULL
END) 'does_not_exist_in_b'
FROM (
SELECT *
FROM tablea a
LEFT JOIN tableb b ON a.propertyID = b.fk_propertyID
UNION
SELECT *
FROM tablea a
RIGHT JOIN tableb b ON a.propertyID = b.fk_propertyID
) result
You need to duplicate your query and use both LEFT and RIGHT joins and UNION the results of those two separate queries to simulate the result of a FULL JOIN.
After simulating this, you need to go over this result set again, by making this a sub-query and then using aggregate functions, COUNT(), along with CASE statements to count how many matches you have in both tables.
Hence, your final queries should look something like this:
SELECT count(CASE
WHEN Property_PropertyID IS NOT NULL
AND Agreement_PropertyID IS NOT NULL
THEN 1
ELSE NULL
END) 'Occupied'
,count(CASE
WHEN Property_PropertyID IS NOT NULL
AND Agreement_PropertyID IS NULL
THEN 1
ELSE NULL
END) 'Vacant'
FROM (
SELECT property.propertyID 'Property_PropertyID'
FROM property
LEFT JOIN agreement ON property.`propertyId` = agreement.`propertyId`
WHERE agreement.`isActive` = '1'
UNION
SELECT agreement.propertyID 'Agreement_PropertyID'
FROM property
RIGHT JOIN agreement ON property.`propertyId` = agreement.`propertyId`
WHERE agreement.`isActive` = '1'
) ResultSet