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.
Related
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.
I have a Relationships table that looks something like this
ID | FromID | ToID | JsonPair
1 10 8 null
2 11 13 null
3 15 21 null
4 26 22 null
And 2 tables From and To
FromID | FromName ToID | ToName
1 'A' 1 'Z'
2 'B' 2 'Y'
... ...
10 'E' 8 'M'
11 'I' ...
... 13 'N'
15 'O' ...
... 21 'F'
26 'U' 22 'H'
I'm trying to update the JsonPair column with Json objects in the form {FromName: ToName}. So the resulting table would look like
ID | FromID | ToID | JsonPair
1 10 8 {'E':'M'}
2 11 13 {'I':'N'}
3 15 21 {'O':'F'}
4 26 22 {'U':'H'}
I'm a novice at SQL. I'm thinking that I should SELECT the names first, then use the result to put in the UPDATE statement.
So far I got this to return FromName and ToName
SELECT F.FromName FROM Relationships AS R
JOIN From as F
ON R.FromID = F.FromID
and
SELECT T.ToName FROM Relationships AS R
JOIN To as T
ON R.FromID = T.FromID;
Then I think I should use the result of this to do
UPDATE Relationships
SET JsonPair = (combine result above and format to json)
WHERE JsonPair IS NULL;
I'm stuck at the combining and format step. Can I get help with this please?
I'm using MySql
You can use the update/join syntax, along with json_object().
Consider:
update relationships r
inner join t_from f on f.fromid = r.fromid
inner join t_to t on t.to_id = r.to_id
set r.jsonpair = json_object(f.fromname, t.toname)
Note: from and to are reserved words in MySQL, hence bad choices for table names. I renamed them to t_from and t_to in the query.
I have the following mysql table with sample data as follows:
id location parentid
1 UK 0
2 East Anglia 1
3 Cambridgeshire 2
4 Norfolk 2
5 Suffolk 2
6 East Midlands 1
7 Derbyshire 6
8 Leicestershire 6
9 EU Countries 0
10 Austria 9
11 Belgium 9
I want to generate a query whereby I can get a list of locations by location name but the location should include any parent location. e.g.
A search for folk should return:
id location
4 Norfolk, East Anglia, UK
5 Suffolk, East Anglia, UK
A search for East should return:
id location
2 East Anglia, UK
6 East Midlands, UK
A search for Bel should return:
id location
11 Belgium
In the above we are excluding concatanting EU countries
Obviously the following doesnt work:
select c.id, CONCAT_WS(', ', c.location, p.location, pp.location) as location
from tbl_locations c
outer left join tbl_locations p on p.id = c.parentid
outer left join tbl_locations pp on pp.id = p.parentid
where c.location like '%whatever%'
If you only want a the parent location this is done with a self join:
select c.id, c.location, p.id, p.location
from tbl_locations c
outer left join tbl_locations p on p.id = c.parentid
where c.location like '%whatever%'
This can be extended (via outer joins) to an arbitrary number of levels but the query will get long. Eg. to three levels:
select c.id, c.location, p.id, p.location, pp.id, pp.location
from tbl_locations c
outer left join tbl_locations p on p.id = c.parentid
outer left join tbl_locations pp on pp.id = p.parentid
where c.location like '%whatever%'
More general recursive queries depend on the details of the RDBMS. The easiest approach is to use a Common Table Expression (CTE). But MySQL doesn't support them (at least, not yet). Other approaches can be used: Generating Depth based tree from Hierarchical Data in MySQL (no CTEs).
1) There is no standard SQL query which can calculate the "transitive closure" a transitive relation. If you want to nest select statements you will always have a maximum depth that can be reached.
2) There is no standard SQL query which will return a row with a variable number of columns. So you would have to format your results in some way or another (e.g. csv).
However, you can accomplish that in MySQL with a stored procedure:
1 CREATE DATABASE IF NOT EXISTS test;
2 USE test;
3
4
5 DROP TABLE IF EXISTS location;
6 CREATE TABLE location (id INT UNSIGNED PRIMARY KEY, name VARCHAR(30) NOT NULL, parent_id INT UNSIGNED NULL REFERENCES location(id));
7
8 INSERT INTO location VALUES
9 (1,"UK",0),
10 (2,"East Anglia",1),
11 (3,"Cambridgeshire",2),
12 (4,"Norfolk",2),
13 (5,"Suffolk",2),
14 (6,"East Midlands",1),
15 (7,"Derbyshire",6),
16 (8,"Leicestershire",6);
17
18
19
20
21 DROP FUNCTION IF EXISTS location_with_parents;
22 DELIMITER //
23 CREATE FUNCTION location_with_parents(location_id INT UNSIGNED) RETURNS VARCHAR(255) READS SQL DATA
24 BEGIN
25 DECLARE LOC_STR VARCHAR(255) DEFAULT NULL;
26 DECLARE LOC_ADD VARCHAR(255) DEFAULT NULL;
27 DECLARE PAR_ID INT UNSIGNED DEFAULT location_id;
28
29 SELECT name INTO LOC_STR FROM location where id=PAR_ID;
30 loop_label: LOOP
31 SELECT parent_id INTO PAR_ID FROM location where id=PAR_ID;
32
33 IF PAR_ID = 0 THEN
34 LEAVE loop_label;
35 ELSE
36 SELECT name INTO LOC_ADD FROM location where id=PAR_ID;
37 SET LOC_STR = CONCAT(LOC_STR, ', ', LOC_ADD);
38 ITERATE loop_label;
39 END IF;
40 END LOOP loop_label;
41 RETURN LOC_STR;
42
43 END;
44 //
45
46 DELIMITER ;
47
48
49
50 SELECT location_with_parents(id) FROM location WHERE name LIKE "%folk%";
51
52 DROP DATABASE test;
Works for me with MySQL 5.6.35
Hope this helps!
Below Query gives you exact result which you want using Recursion method.
Select S.ID ,
concat( S.location,',', Group_concat
(distinct A.location ORDER BY A.location SEPARATOR ',' ) ) as location
from
( SELECT distinct #r AS _id ,location,
(
SELECT #r := parentid
FROM tbl_locations
WHERE id = _id
) AS parentid,
#l := #l + 1 AS level
FROM (
SELECT #r := h.ID,
#l := 0,
#cl := 0
from tbl_locations h
where location like '%folk%'
) vars,
tbl_locations h
WHERE #r <> 0
)A , tbl_locations S
where s.location like '%folk%'
group by S.ID
OutPut :
location like '%East%' :
location like '%Folk%'
its good question and check and ask if you have any concerns.
kon
id | name
1 alex
2 peter
3 john
ticket
id | amount | kon_id | package
122 13 1 234
123 12 1 234
124 20 2 NULL
125 23 2 235
126 19 1 236
I would like to get a list of all contacts with the sum of the amount, except tickets, where the package entry is NULL.
My problem is, that I only get the contacts which have a ticket, because of the WHERE clause.
SELECT
kon.id,
kon.name,
SUM(ticket.amount)
FROM kon LEFT JOIN ticket ON kon.id = ticket.kon_id
WHERE ticket.package IS NOT NULL
GROUP BY kon.id
At the moment, the output looks like this
1 alex 44
2 peter 23
but it should look like this
1 alex 44
3 john NULL
2 peter 23
I use a MySQL Server.
Is it possible to solve this?
Replace Where with AND
SELECT
kon.id,
kon.name,
SUM(ticket.amount)
FROM kon LEFT JOIN ticket ON kon.id = ticket.kon_id AND ticket.package IS NOT NULL
GROUP BY kon.id
Check This.
SELECT
k.id,
k.name ,
coalesce (SUM(t.amount) ,0)
FROM kon k LEFT JOIN
( select id,amount,kon_id,package from ticket where package is not null ) t
ON k.id = t.kon_id
GROUP BY k.id, k.name
OutPut :
Begin Tran
Create Table #Kon (id INt , name Nvarchar(255))
Insert into #Kon
Select 1,'alex' UNION ALL
Select 2,'peter' UNION ALL
Select 3,'john'
Create Table #Ticket (id int,amount int,Kon_Id Int,Package Int)
INSERT INTO #Ticket
SELECT 122,13,1,234 UNION ALL
SELECT 123,12,1,234 UNION ALL
SELECT 124,20,2,NULL UNION ALL
SELECT 125,23,2,235 UNION ALL
SELECT 126,19,1,236
SELECT K.id, Name,SUM(amount) amount
FROM #Kon k
LEFT JOIN #Ticket T ON K.id=T.Kon_Id
GROUP BY K.id,Name
RollBAck Tran
Generally, "ticket.package IS NOT NULL" is wrong condition: your query becomes inner join from left join. If ticket.package should be NOT NULL to add from amount, it should be not in condition, but inside SUM agregate function.
working example for MS SQL
SELECT
kon.id,
min(kon.name),
SUM(case when package is NULL then 0 else ticket.amount end)
FROM #kon kon LEFT JOIN #ticket ticket ON kon.id = ticket.kon_id
GROUP BY kon.id
Answer from Mr. Bhosale is right too, but for big tables will have worse performance (the reason is subquery)
the following query return your expected result
SELECT
kon.id,
kon.name,
SUM(ticket.amount) as 'amount'
FROM kon LEFT JOIN ticket ON kon.id = ticket.kon_id
GROUP BY kon.id, kon.name
attached image shows the result
I figured out the fastest way to solve the problem. It takes about 0.2s compared to the other solutions (2s - 2min). The CAST is important, otherwise the summation of double variables is wrong (float string problem).
SELECT
kon1,
kon2,
SUM(CAST(kon3 AS DECIMAL(7,2)))
FROM (
SELECT k.id kon1, k.name kon2, t.amount kon3 FROM kon as k
LEFT JOIN ticket t ON k.id = t.ticket_kon
WHERE t.package IS NOT NULL
UNION ALL
SELECT k.id kon1, k.name kon2, NULL kon3 FROM kon k WHERE) t1
GROUP BY kon1, kon2
*None of other available answers solved my problem
I have a table t like this
id,cc,count
'1','HN','22'
'1','US','18'
'1','VN','1'
'2','DK','2'
'2','US','256'
'3','SK','1'
'3','US','66310'
'4','UA','2'
'4','US','263'
'6','FR','7'
'6','US','84'
'9','BR','3'
I want to get the rows for ids with maximum count, like below:
id,cc,count
'1','HN','22'
'2','US','256'
'3','US','66310'
'4','US','263'
'6','US','84'
'9','BR','3'
My current code is like this but I am not getting the expected results:
SELECT t.* FROM t
JOIN (
SELECT
t.id,t.cc
,max(t.count) as max_slash24_count
FROM t
group by t.id,t.cc
) highest
ON t.count = highest.max_slash24_count
and t.cc = highest.cc
Can anybody help me out?
Remove CC column from group by. Try this.
SELECT t.* FROM t
JOIN (
SELECT
t.id
,max(t.count) as max_slash24_count
FROM t
group by t.id
) highest
ON t.count = highest.max_slash24_count
and t.id= highest.id
Try this:
create table t (id varchar(10), cc varchar(10), count varchar(10))
insert into t (id,cc,count) values ('1','HN','22');
insert into t (id,cc,count) values ('1','US','18');
insert into t (id,cc,count) values ('1','VN','1');
insert into t (id,cc,count) values ('2','DK','2');
insert into t (id,cc,count) values ('2','US','256');
insert into t (id,cc,count) values ('3','SK','1');
insert into t (id,cc,count) values ('3','US','66310');
insert into t (id,cc,count) values ('4','UA','2');
insert into t (id,cc,count) values ('4','US','263');
insert into t (id,cc,count) values ('6','FR','7');
insert into t (id,cc,count) values ('6','US','84');
insert into t (id,cc,count) values ('9','BR','3');
select *
from t
where exists (
select *
from t as t1
group by t1.id
having t1.id = t.id and max(t1.count) = t.count
)
Result
ID CC COUNT
-------------
1 HN 22
2 US 256
3 US 66310
4 US 263
6 US 84
9 BR 3
Check SQLFiddle
This question was answered a lot of times on SO. The query is as simple as this:
SELECT m.id, m.cc, m.count
FROM t m # "m" from "max"
LEFT JOIN t b # "b" from "bigger"
ON m.id = b.id # match a row in "m" with a row in "b" by `id`
AND m.count < b.count # match only rows from "b" having bigger count
WHERE b.count IS NULL # there is no "bigger" count than "max"
The real issue on your question is about the column types. If count is char (and not int) then the string comparison happens using the dictionary order, not the numeric order.
For example, if the third row reads:
'1','VN','123'
you might expect it to be selected in the output, because 123 is bigger than 22. This does not happen because, as string, '123' is smaller than '22'.
Even tho, this was already answered, using ROW_NUMBER functionality as in SQL Server is quite fun and interesting: please look at this query:
SELECT TT.Id, TT.cc, TT.count
FROM (
SELECT t.cc
, t.count
, #row_number:=CASE WHEN #Id=Id THEN #row_number+1 ELSE 1 END AS row_number
, #Id:=Id AS Id
FROM t, (SELECT #row_number:=0, #Id:='') AS temp
ORDER BY t.Id, t.count DESC
) AS TT
WHERE TT.row_number = 1
ORDER BY TT.Id;
It produces expected output:
| Id | cc | count |
|----|----|-------|
| 1 | HN | 22 |
| 2 | US | 256 |
| 3 | US | 66310 |
| 4 | US | 263 |
| 6 | US | 84 |
| 9 | BR | 3 |
SQLFiddle
I've taken test data from #Andrey Morozov