SQL JOINs with generic and specific mappings - mysql

From every row from the main table, I want to obtain the ColWanted value from the mapping table. For example, for the main table row with id 100 I should obtain 'one', for the id 101 I should obtain 'two', for the 102 'one', 103 'one', etc.
Is it possible?
Main table
PkId
CAT
UP
100
1
1
101
1
2
102
2
1
103
1
3
Mapping table
CAT
UP
ColWanted
1
null
one
1
2
two
2
null
one
Update 1
I tried proposed solutions, like:
SELECT m.PkId, m.CAT mCAT, m.UP mUP, p.ColWanted pColWanted, p.CAT pCAT, p.UP pUP
FROm m
LEFT JOIN p ON
m.CAT = p.CAT AND
m.Up = COALESCE(p.Up, m.Up)
WHERE m.PkId = 101
PkId
mCAT
mUP
pColWanted
pCAT
pUP
101
1
2
one
1
null
101
1
2
two
1
2
And it doesn't work as expected. It should return only one row with pColWanted = 'two', but returns two rows. If a specific row (with UP) exist in the mapping table, it shouldn't return the more generic row (without UP).

An empty string or NULL in the MappingTable is considered matching any value.
select m.PkId, p.ColWanted
from MainTable m
left join MappingTable p on (m.CAT = p.CAT or p.CAT is null or p.CAT ='' )
and (m.UP = p.UP or p.UP is null or p.UP ='')

Here's another solution:
SELECT m.PkId, p.ColWanted
FROM MainTable m
JOIN MappingTable p ON m.CAT = p.CAT AND m.UP = COALESCE(p.UP, m.UP)
Obvious m.UP = m.UP is guaranteed to be true. So when p.UP is NULL, the default is that the match is true.

Related

SQL : Record Existance Check in another table

My requirement is below .
I have two tables let's call them Table A and Table B :
PARTNER_ID PARTNER_Registration Partner_PANNUMBER
----------
1 11 AB1
2 22 AB2
3 33 AB3
4 44 AB4
5 55 AB5
6 66 AB6
7 77 AB5
8 88 AB8
i Will have another table B which contains PID , Preg, Ppan as follows
PID PREG PPAN
----------
1 11 AB1
2 22 AB2
3 33 AB3
4 44
5 AB5
66 AB6
Now I should create a column Output in table A and have output as follows
PARTNER_ID PARTNER_Registration Partner_PANNUMBER Output
----------
1 11 AB1 All three Found
2 22 AB2 All three Found
3 33 AB3 All three found
4 44 AB4 PPAN NOT FOUND
5 55 AB5 PARTNER_Registration Not Found in TABLE B
6 66 AB6 PARTNER_ID Not found in Table B
7 77 AB5 PARTNER_ID, PARTNER_Registration Not found in Table B
8 88 AB8 None of them Found in Table B
Can some one help me find an easy way to acheive this in SQL,
I would like to populate which values of 3 columns are not present in another and update output column accordingly..
Thanks
I would just add up the number of matches:
select a.*,
( exists (select 1 from b where b.PID = a.PARTNER_ID) +
exists (select 1 from b where b.PREG = a.PARTNER_Registration) +
exists (select 1 from b where b.PPAN = a.Partner_PANNUMBER)
) as num_matches
from a;
You can use multiple LEFT JOIN with table B, and test which ones produce NULL values.
SELECT a.*,
CASE WHEN b1.pid IS NOT NULL AND b2.preg IS NOT NULL AND b3.ppan IS NOT NULL
THEN 'All three found'
WHEN b1.pid IS NULL AND b2.preg IS NULL AND p3.ppan IS NULL
THEN 'None of them found in Table B'
ELSE CONCAT(CONCAT_WS(', '
IF(b1.pid IS NULL, 'Partner_ID', NULL),
IF(b2.preg IS NULL, 'Partner_Registration', NULL),
IF(b3.ppan IS NULL, 'PPAN', NULL)),
' not found in Table B') AS Output
FROM TableA AS a
LEFT JOIN TableB AS b1 ON a.partner_id = b1.pid
LEFT JOIN TableB AS b2 ON a.partner_registration = b2.preg
LEFT JOIN TableB AS b3 ON a.partner_pannumber = b.ppan
CONCAT_WS() will ignore null values, so with the IF statements inverting NULL with the names of the missing values, you get the list of results you want.
I would use multiple LEFT JOIN because of the use case of OP where we can manipulate the null values means missing in table2 to achieve the exact output you want.
The query looks big but it is just manipulating the string using specific string functions to get the final string output.
select a.partner_id
,a.partner_registration
,a.partner_pannumber
,case
when chk = ''
then 'All three found'
else
concat(case (length(chk) - length(replace(chk,',','')))
when 3
then 'None of them found'
when 1
then replace(chk,',',' not found')
else
regexp_replace(chk,'[,]',' not found',1,2)
end
,' in table2'
)
end Remarks
from
(
select a.*
,concat(case when pi.pid is null then 'Partner Id,' else '' end
,case when pr.preg is null then 'Partner Registration,' else '' end
,case when pp.ppan is null then 'PPAN,' else '' end
) chk
from table1 a
left join table2 pi
on a.partner_id = pi.pid
left join table2 pr
on a.partner_registration = pr.preg
left join table2 pp
on a.partner_pannumber = pp.ppan
) a
P.S. I personally like the answer with usage of concat_ws as it has less code but you need a little modification for non of them match in ... case.

List join column as null value if there is no match in the other table

My problem is simple: I want to list everything from my "form" table and if there aren't any matches to this "form" in the "outstanding" table, then this row should be also listed only with NULL in the PAYED_GROSS_AMOUNT column (the "outstanding" table is used for example for this column). Right now with this query all I get are the "form" matches, which have also matching entries in the "outstanding" table:
SELECT
`f`.`ID` AS `ID`,
`f`.`FORM_NR` AS `form_nr`,
`f`.`DELIVERY_DATE` AS `delivery_date`,
`f`.`FORM_DATE` AS `form_date`,
`f`.`PAYMENT_DATE` AS `payment_date`,
MAX(`os`.`PAYED_DATE`) AS `payed_date`,
`fi`.`GROSS_MONEY` AS `gross_money`,
`fi`.`NET_PRICE` AS `net_price`,
ifnull(SUM(`os`.`PAYED_GROSS_AMOUNT`), 0) AS `payed_gross_amount`,
ifnull((`fi`.`GROSS_MONEY`
- SUM(`os`.`PAYED_GROSS_AMOUNT`)),
`fi`.`GROSS_MONEY`) AS `remaining_amount`
FROM `form` `f`
LEFT JOIN `outstanding` `os` ON `f`.`ID` = `os`.`INVOICE_ID`,
(SELECT form_id AS `FORM_ID`,
SUM(gross_money) AS `GROSS_MONEY`,
SUM(net_price) AS `NET_PRICE`
FROM form_item
GROUP BY form_id) fi
WHERE `f`.SUBTYPE <> 3
AND `fi`.FORM_ID = `f`.ID
AND `f`.STATUS = 2
AND `f`.DIRECTION = 1
AND `os`.DELETED <> 'deleted'
AND (`f`.PAYMENT_TYPE = 2 OR `f`.PAYMENT_TYPE = 4)
AND `f`.FORM_TYPE = 'Invoice'
AND `f`.deleted <> 'deleted'
GROUP BY `f`.`ID`,
`fi`.`form_id`
ORDER BY `f`.`FORM_DATE` DESC;
I also tried with LEFT OUTER JOIN but with no luck :(
Thank you!
EDIT:
Query simplified as possible (you can ignore the WHERE clauses in the end)
Expected result:
form table:
ID FORM_NR ...
1 2019/1
2 2019/2
3 2019/4
4 2019/7
...
outstanding table:
INVOICE_ID PAYED_DATE PAYED_GROSS_AMOUNT ...
2 2019-02-05 100
3 2019-02-06 200
...
Result:
FORM_NR FORM_DATE ... PAYED_DATE PAYED_GROSS_AMOUNT
2019/1 2019-02-01 null 0
2019/2 2019-02-02 2019-02-05 100
2019/4 2019-02-03 2019-02-06 200
2019/7 2019-02-04 null 0
...
PAYED_GROSS_AMOUNT is because of the IFNULL method 0 and not NULL.
Use NOT EXISTS. Something like this:
select . . .
from form f
where <conditions on form> and
not exists (select 1
from outstanding o
where o.form_item = f.id and
<conditions on outstanding>
);

MySQL - select product_id from 2 different tables and group results

situation:
table 1 - #__virtuemart_products
virtuemart_product_id | product_special
PRODUCTS_IDS | 0 or 1
table 2 - #__virtuemart_product_badges
virtuemart_product_id | product_badge
PRODUCTS_IDS | for this situation code 3
I have a default SQL
SELECT p.`virtuemart_product_id`
FROM `#__virtuemart_products` as p
WHERE p.`product_special` = 1;
results is product IDs like 2,3,225,...
I need modify this SQL syntax for select IDs from 2 different tables and return one column.
If I modify syntax like that:
SELECT p.`virtuemart_product_id`, badges_table.`virtuemart_product_id`
FROM `#__virtuemart_products` as p, `#__virtuemart_product_badges` as badges_table
WHERE p.`product_special` = 1 OR badges_table.`badge` = 3
Result is:
virtuemart_product_id | virtuemart_product_id
1 | 123
1 | 321
1 | 231
....
why is first column 1,1,1,...? here must be product_id, no product_special code
I need group this results into one column virtuemart_product_id
What I doing wrong?
I think what you are looking for is UNION of the IDs fetched from two different tables.
SELECT p.`virtuemart_product_id`, badges_table.`virtuemart_product_id`
FROM `#__virtuemart_products` as p, `#__virtuemart_product_badges` as
badges_table
WHERE p.`product_special` = 1 OR badges_table.`badge` = 3
What the above query is doing is, it is performing a join between the two tables with the condition that product_special should be 1 or badge should be 3. Hence, each row from one table will be joined with each row of the other table where the condition will satisfy.
To get IDs from both the tables you can get the results from each table according to condition and then perform a UNION on them. So for example
(SELECT `virtuemart_product_id` FROM `#__virtuemart_products` WHERE
`product_special` = 1)
UNION
(SELECT `virtuemart_product_id` FROM
`#__virtuemart_product_badges` WHERE `badge` = 3)
I hope this helps.

Conditional condition in ON clause

I am trying to apply a conditional condition inside ON clause of a LEFT JOIN. What I am trying to achieve is somewhat like this:
Pseudo Code
SELECT * FROM item AS i
LEFT JOIN sales AS s ON i.sku = s.item_no
AND (some condition)
AND (
IF (s.type = 0 AND s.code = 'me')
ELSEIF (s.type = 1 AND s.code = 'my-group')
ELSEIF (s.type = 2)
)
I want the query to return the row, if it matches any one of the conditions (Edit: and if it matches one, should omit the rest for the same item).
Sample Data
Sales
item_no | type | code | price
1 0 me 10
1 1 my-group 12
1 2 14
2 1 my-group 20
2 2 22
3 2 30
4 0 not-me 40
I want the query to return
item_no | type | code | price
1 0 me 10
2 1 my-group 20
3 2 30
Edit: The sales is table is used to apply special prices for individual users, user groups, and/or all users.
if type = 0, code contains username. (for a single user)
if type = 1, code contains user-group. (for users in a group)
if type = 2, code contains empty-string (for all users).
Use the following SQL (assumed, the the table sales has a unique id field as usual in yii):
SELECT * FROM item AS i
LEFT JOIN sales AS s ON i.sku = s.item_no
AND id = (
SELECT id FROM sales
WHERE item_no = i.sku
AND (type = 0 AND code = 'me' OR
type = 1 AND code = 'my-group' OR
type = 2)
ORDER BY type
LIMIT 1
)
Try following -
SELECT *,SUBSTRING_INDEX(GROUP_CONCAT(s.type ORDER BY s.type),','1) AS `type`, SUBSTRING_INDEX(GROUP_CONCAT(s.code ORDER BY s.type),','1) AS `code`,SUBSTRING_INDEX(GROUP_CONCAT(s.price ORDER BY s.type),','1) AS `price`
FROM item AS i
LEFT JOIN sales AS s
ON i.sku = s.item_no AND (SOME CONDITION)
GROUP BY i.sku

MySQL: Return all rows of table A and true|false if record exists in table B

I have a sales_cat table, and a user_cat table.
sales_cat
id_cat
name
user_cat
id
id_user
id_cat
I need to get all the sales_cat rows, joined with the user_cat table for a specific user, indicating if that user has or not the category. Example, for id_user = 4 it should return:
id_cat | name | selected
1 | kids | 1
2 | men | 1
3 | women | 0
Of course, the "selected" field is actually a value that depends on the existence of a linked record in user_cat. I've set a table structure in sqlfiddle.
My current solution only returns the linked data:
SELECT sales_cat.id_cat, sales_cat.name
FROM sales_cat
LEFT JOIN user_cat ON user_cat.id_cat = sales_cat.id_cat
WHERE user_cat.id_user = 4
...which is returning:
id_cat | name
1 | kids
2 | men
I'm still missing the "selected" column and the 3 | women row.
Any ideas? Thanks!
Try Like This
SELECT sales_cat.id_cat, sales_cat.name,case when
user_cat.id is null then 0 else 1 end as "selected" FROM
sales_cat LEFT JOIN user_cat ON
user_cat.id_cat = sales_cat.id_cat and
user_cat.id_user = 4
http://sqlfiddle.com/#!2/395ba/25 Thanks #Phil
Try this:
select distinct
s.id_cat,
s.name,
case when u.id_user is null then 0 else 1 end selected
from sales_cat s
left join user_cat u on s.id_cat = u.id_cat
and (u.id_user = 4 or u.id_user is null)
While the approach is similar to Satson's answer, we move the null check from user_cat to id_user.
You can try this:
SELECT sales_cat.id_cat, sales_cat.name, case when user_cat.id_user is not null then 1 else 0 end selected
FROM sales_cat
LEFT JOIN user_cat ON (user_cat.id_cat = sales_cat.id_cat and user_cat.id_user = 4)