Consider the below table
CREATE TABLE `temp` (
`id` int(11) NOT NULL,
`lang` char(2) COLLATE utf8_unicode_ci NOT NULL,
`channel` char(2) COLLATE utf8_unicode_ci NOT NULL,
`name` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`,`lang`,`channel`)
)
insert into `temp` (`id`, `lang`, `channel`, `name`) values('1','fr','ds','Jacket');
insert into `temp` (`id`, `lang`, `channel`, `name`) values('1','en','ds','Jacket');
insert into `temp` (`id`, `lang`, `channel`, `name`) values('2','en','ds','Jeans');
insert into `temp` (`id`, `lang`, `channel`, `name`) values('3','en','ds','Sweater');
insert into `temp` (`id`, `lang`, `channel`, `name`) values('1','de','ds','Jacket');
The question is how can I find which entries with lang en do not exist for fr?
My head is stuck and I believe this to be a trivial query but I am having one of these days.
There are several ways of achieving this. One way is to use a sub-select with not exists clause:
select id, channel
from temp t1
where t1.lang = 'en'
and not exists (
select 1
from temp t2
where t2.lang = 'fr'
and t1.id = t2.id
)
Alternatively, you can use an outer join:
select t1.id, t1.channel
from temp t1
left outer join temp t2 on t1.id = t2.id
where t1.lang = 'en'
and t2.id is null
based on #AleksG
SELECT t1.id, t1.channel, t1.name
FROM temp t1
LEFT JOIN temp t2 ON t1.id = t2.id AND t2.lang = 'fr'
WHERE t1.lang = 'en' AND t2.lang IS NULL
You can do this with aggregation:
select t.channel, t.name
from temp t
group by t.channel, t.name
having sum(case when t.lang = 'fr' then 1 else 0 end) = 0 and
sum(case when t.lang = 'en' then 1 else 0 end) > 0;
The first condition in the having clause counts up the number of times that French appears. The second counts up the number of times that English appears. When there are none for French and at least one for English, then the channel and name are in the result set.
Related
Given the following MySQL 8 table structures...
CREATE TABLE `products` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(1000) NOT NULL,
PRIMARY KEY (`id`)
);
/* the "names" of the custom attributes that could exist for any given product */
CREATE TABLE `custom_attributes` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
);
/* the custom attributes of products stored as json array, when "key" is FK to custom_attributes table, and "value" is the attribute value for the given product */
/* in an ideal design, this table would not exist as there is a one-to-one relationship with `product`, and so could have simply been an `attributes` column in the product table, but we are where we are... */
CREATE TABLE `product_custom_attributes` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`product_id` bigint unsigned NOT NULL,
`attribute_value` json DEFAULT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `product_custom_attributes_product_id_foreign` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`)
);
...and the following data...
INSERT INTO products (`id`, `name`) VALUES (1, 'Big Blue Ball');
INSERT INTO products (`id`, `name`) VALUES (2, 'Small Wooden Bat');
INSERT INTO custom_attributes (`id`, `title`) VALUES (1, 'Size');
INSERT INTO custom_attributes (`id`, `title`) VALUES (2, 'Color');
INSERT INTO custom_attributes (`id`, `title`) VALUES (3, 'Material');
INSERT INTO custom_attributes (`id`, `title`) VALUES (4, 'Height');
INSERT INTO product_custom_attributes (`id`, `product_id`, `attribute_value`) VALUES (1, 1, '{"1": "Big", "2": "Blue"}');
INSERT INTO product_custom_attributes (`id`, `product_id`, `attribute_value`) VALUES (2, 2, '{"1": "Small", "3": "Wood"}');
...how can I write a query to return the following results...?
product_id
name
Size
Color
Material
Height
1
Big Blue Ball
Big
Blue
2
Small Wooden Bat
Small
Wood
I cannot predict, at runtime, what custom_attributes might exist for products because it's likely this list will change over time, e.g. new attributes added.
I'm pretty sure I'll need to use the JSON_TABLE() function, but I've struggled to get result with the unpredicatable attributes (and therefore unpredictable JSON "columns") in play.
Edit - Solution
SET SESSION group_concat_max_len=4294967295;
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(IF(ca.title = ''',
ca.title,
''', attribute_values.attribute_value, NULL)) AS `',
ca.title, '`'
)
) INTO #sql
FROM
products p
LEFT OUTER JOIN custom_attributes ca ON p.project_id = ca.project_id
LEFT OUTER JOIN product_custom_attributes pca ON p.id = pca.product_id
LEFT OUTER JOIN JSON_TABLE(
JSON_KEYS(pca.attribute_value),
'$[*]' COLUMNS (
rn FOR ORDINALITY,
attribute_key VARCHAR(255) PATH '$'
)
) attribute_keys ON ca.id = attribute_keys.attribute_key
LEFT OUTER JOIN JSON_TABLE(
JSON_EXTRACT(pca.attribute_value, '$.*'),
'$[*]' COLUMNS (
rn FOR ORDINALITY,
attribute_value VARCHAR(255) PATH '$'
)
) attribute_values ON attribute_values.rn = attribute_keys.rn;
-- if there are no custom attributes #sql will be NULL and when appended to the following query will make the whole thing NULL, so deal with this...
SET #sql = CASE WHEN ISNULL(#sql) THEN '' ELSE CONCAT(', ', #sql) END;
SET #sql = CONCAT('
SELECT
p.id
, p.name
', #sql, '
FROM
products p
LEFT OUTER JOIN custom_attributes ca ON p.project_id = ca.project_id
LEFT OUTER JOIN product_custom_attributes pca ON p.id = pca.product_id
LEFT OUTER JOIN JSON_TABLE(
JSON_KEYS(pca.attribute_value),
''$[*]'' COLUMNS (
rn FOR ORDINALITY,
attribute_key VARCHAR(255) PATH ''$''
)
) attribute_keys ON ca.id = attribute_keys.attribute_key
LEFT OUTER JOIN JSON_TABLE(
JSON_EXTRACT(pca.attribute_value, ''$.*''),
''$[*]'' COLUMNS (
rn FOR ORDINALITY,
attribute_value VARCHAR(255) PATH ''$''
)
) attribute_values ON attribute_values.rn = attribute_keys.rn
GROUP BY p.id, p.name');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
In the query below I am "JOINING" another table where i.isPrimary > 0 and if all i.isPrimary are 0 I just get the first result.
The result set from the query is as expected, but I want to bring more values from each subselect.
I am getting the error: SQL Error (1241): Operand should contain 1 column(s).
How can this query be rewritten in order to get more results from each subselect?
Thanks
-- borrowed from https://stackoverflow.com/q/7745609/808921
CREATE TABLE IF NOT EXISTS `ResearchEntity` (
`id` int(6) unsigned NOT NULL,
`name` varchar(200) NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `ResearchEntity` (`id`, `name`) VALUES
('1', 'one'),
('2', 'two'),
('3', 'three');
CREATE TABLE IF NOT EXISTS `ProfileImageEntity` (
`id` int(6) unsigned NOT NULL,
`isPrimary` int(1) unsigned NOT NULL,
`value` varchar(200) NOT NULL,
`researchId` int(2) unsigned NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `ProfileImageEntity` (`id`,`isPrimary`, `value`,`researchId`) VALUES
('1', 0, 'not primary',1),
('2', 0, 'not primary',1),
('3', 1, 'primary!!!',1),
('4', 0, 'primary!!!',2),
('5', 0, 'not primary',2),
('6', 0, 'not primary',2)
;
CREATE TABLE IF NOT EXISTS `UserNameEntity` (
`id` int(6) unsigned NOT NULL,
`isPrimary` int(1) unsigned NOT NULL,
`value` varchar(200) NOT NULL,
`researchId` int(2) unsigned NOT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
INSERT INTO `UserNameEntity` (`id`,`isPrimary`, `value`,`researchId`) VALUES
('1', 0, 'first one, should be returned',1),
('2', 0, 'not primary',1),
('3', 0, 'primary',1),
('4', 1, 'primary',3),
('5', 0, 'not primary',3),
('6', 0, 'not primary',3);
SQL FIDDLE
: http://sqlfiddle.com/#!9/028218/1
SELECT r.*,
(SELECT i.id FROM ProfileImageEntity i WHERE i.researchId = r.id ORDER BY i.isPrimary DESC, i.id ASC LIMIT 1 ) AS primaryImageId,
(SELECT i.id FROM UserNameEntity i WHERE i.researchId = r.id ORDER BY i.isPrimary DESC, i.id ASC LIMIT 1 ) AS primaryImageId
FROM ResearchEntity r
ORDER BY id DESC;
What I understood from your question and comment that you want more columns from sub Query which is not possible. So try this query:
It is easy in MySql 8 but you are using MySql 5.7 where it a little bit tricky So try this:
select
t1.*,
t2.id AS primaryImageId,
t2.value AS primaryImageValue,
t3.id AS primaryUserId,
t3.value AS primaryUserValue
from ResearchEntity t1
left join (
SELECT *,
IF(researchId=#last,#_seq:=#_seq+1,#_seq:=1) AS rn,
#last:=researchId
FROM ProfileImageEntity , (SELECT #_seq:=1, #last:=0) r
ORDER BY researchId,isPrimary DESC, id ASC
) t2 on t1.id=t2.researchId and t2.rn=1
left join (
SELECT *,
IF(researchId=#last,#_seq:=#_seq+1,#_seq:=1) AS rn,
#last:=researchId
FROM UserNameEntity , (SELECT #_seq:=1, #last:=0) r
ORDER BY researchId,isPrimary DESC, id ASC
) t3 on t1.id=t3.researchId and t3.rn=1
order by t1.id
DEMO
In MySql 8 using row_number()
with cte as (
SELECT *,
row_number() over (partition by researchId ORDER BY isPrimary DESC, id ASC) rn
FROM ProfileImageEntity
),
cte1 as (
sELECT *,
row_number() over (partition by researchId ORDER BY isPrimary DESC, id ASC) rn
FROM UserNameEntity
)
select
t1.*,
t2.id AS primaryImageId,
t2.value AS primaryImageValue,
t3.id AS primaryUserId,
t3.value AS primaryUserValue
from ResearchEntity t1 left join cte t2 on t1.id=t2.researchId and t2.rn=1
left join cte1 t3 on t1.id=t3.researchId and t3.rn=1
try left join
SELECT r.*,i.id FROM ResearchEntity r left join ProfileImageEntity i on r.id = i.researchId
ORDER BY i.isPrimary,i.id DESC;
you just need to left join 2 times
SELECT r.*,i.id,j.id FROM ResearchEntity r left join ProfileImageEntity i on r.id = i.researchId left join UserNameEntity j on r.id=j.researchId ORDER BY i.isPrimary,i.id DESC;
I have following requirements.
I have two tables T1 and T2 like
Table T1
Product Geography
P1 G1
P2 G1
P2 G2
Table T2
Product Geography
P1 G1, G2
P2 G1, G2
I want a query to get data from table T2 if comma separated Geography have exactly matching records in T1. If there are less or more geographies in any table than it should not return that row. Sequence of geographies in T2 is not fixed. So return of that query from above example will be:
Product Geography
P2 G1, G2
Join on concating values
SELECT t2.Product,t2.geography FROM t2
JOIN
(SELECT t1.Product,GROUP_CONCAT(t1.geography ORDER BY t1.Geography SEPARATOR ', ') as concatgeo FROM t1
GROUP BY t1.product)x
ON t2.Geography=x.concatgeo AND t2.Product=x.Product
SELECT
firstTable.Product,
firstTable.geographies
FROM
(
SELECT
Product,
REPLACE(GROUP_CONCAT(Geography),' ','') AS geographies
FROM T1
GROUP BY Product) firstTable
INNER JOIN
(
SELECT
Product,
REPLACE(Geography,' ','') AS geographies
FROM T2 ) secondTable
ON firstTable.Product = secondTable.Product
WHERE firstTable.geographies = secondTable.geographies;
Note: I've replaced the spaces using REPLACE function just to ensure the two queries from two tables generate the same string
SQL FIDDLE DEMO
TEST (If you cannot access sqlfiddle):
DROP TABLE IF EXISTS `t1`;
CREATE TABLE `t1` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`Product` varchar(50) CHARACTER SET utf8 NOT NULL,
`Geography` varchar(100) CHARACTER SET utf8 NOT NULL,
PRIMARY KEY (`ID`)
);
INSERT INTO `t1` VALUES ('1', 'P1', 'G1');
INSERT INTO `t1` VALUES ('2', 'P2', 'G1');
INSERT INTO `t1` VALUES ('3', 'P2', 'G2');
DROP TABLE IF EXISTS `t2`;
CREATE TABLE `t2` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`Product` varchar(50) CHARACTER SET utf8 NOT NULL,
`Geography` varchar(100) CHARACTER SET utf8 NOT NULL,
PRIMARY KEY (`ID`)
);
INSERT INTO `t2` VALUES ('1', 'P1', 'G1, G2');
INSERT INTO `t2` VALUES ('2', 'P2', 'G1, G2');
Running the above query on this test data you will get output like below:
Product geographies
P2 G1,G2
I have a scenario in which I have to check whether a data already exist in a table or not. If a user n application already exists then we don't need to perform any operation else perform insertion.
CREATE TABLE `table1` (
`ID` INT(11) NOT NULL AUTO_INCREMENT,
`GroupName` VARCHAR(100) DEFAULT NULL,
`UserName` VARCHAR(100) DEFAULT NULL,
`ApplicationName` VARCHAR(100) DEFAULT NULL,
`UserDeleted` BIT DEFAULT 0,
PRIMARY KEY (`ID`)
)
CREATE TABLE `temp_table` (
`ID` INT(11) NOT NULL AUTO_INCREMENT,
`GroupName` VARCHAR(100) DEFAULT NULL,
`UserName` VARCHAR(100) DEFAULT NULL,
`ApplicationName` VARCHAR(100) DEFAULT NULL,
PRIMARY KEY (`ID`)
)
table1 is the main table while temp_table from which I have to perform comparision.
Sample date script:
INSERT INTO `table1`(`ID`,`GroupName`,`UserName`,`ApplicationName`,`userdeleted`)
VALUES
('MidasSQLUsers','Kevin Nikkhoo','MySql Application',0),
('MidasSQLUsers','adtest','MySql Application',0),
('Salesforce','Kevin Nikkhoo','Salesforce',0),
('Salesforce','devendra talmale','Salesforce',0);
INSERT INTO `temp_table`(`ID`,`GroupName`,`UserName`,`ApplicationName`,`userdeleted`)
VALUES
('MidasSQLUsers','Kevin Nikkhoo','MySql Application',0),
('MidasSQLUsers','adtest','MySql Application',0),
('Salesforce','Kevin Nikkhoo','Salesforce',0),
('Salesforce','Kapil Singh','Salesforce',0);
Also if a row of temp_table does not exist int table1 then its status should be as userdeleted 1 as i mentioned in desired output.
Result: table1
ID GroupName UserName ApplicationName Deleted
1 MidasSQLUsers Kevin Nikkhoo MySql Application 0
2 MidasSQLUsers adtest MySql ApplicationName 0
3 Salesforce Kevin Nikkhoo Salesforce 0
4 Salesforce devendra talmale Salesforce 1
5 SalesForce Kapil Singh Salesforce 0
Please help
this should do the trick, change whatever is inside the concat, to met your specification.
first do one update to "delete" the rows that are not in tempTable:
update table t1 set deleted = 'YES'
where concat( t1.groupName, t1.Username, t1.application) NOT IN
(select concat( t2.groupName, t2.Username, t2.application) from tempTable t2);
second: insert the new records
insert into table1 t1 (t1.groupName, t1.username, t1.application_name, t1.deleted)
(select t2.groupName, t2.Username, t2.application, t2.deleted from tempTable t2
where concat(t2.userName, t2.application, t2.groupName, t2.deleted) **not in**
(select concat(t3.userName, t3.application, t3.groupName, t3.deleted)
from table1 t3)
the concat function will make a new row from existing rows... this way I can compare multiple fields at same time as if I was comparing only one...
the "not in" lets make you a query inside a query...
Slight variation on the above queries.
Using JOINs, which if you have an index on the userName, application and groupName fields will likely be faster
UPDATE table1 t1
LEFT OUTER JOIN temp_table t2
ON t1.userName = t2.userName
AND t1.application = t2.application
AND t1.groupName = t2.groupName
SET t1.deleted = CASE WHEN t2.ID IS NULL THEN 1 ELSE 0 END
For the normal insert
INSERT INTO table1 t1 (t1.groupName, t1.username, t1.application_name, t1.deleted)
(SELECT t2.groupName, t2.Username, t2.application, t2.deleted
FROM tempTable t2
LEFT OUTER JOIN table1 t3
ON t2.userName = t3.userName
AND t2.application = t3.application
AND t2.groupName = t3.groupName
WHERE t3.ID IS NULL)
I have a column that i am looking to retrieve all matches of in one row. I am querying other data as well. Currently i am using group_concat. This has worked great until now. Sometimes there are potential NULL values in this column and this has been preventing anything from being returned.
i have tried various other solutions posted here without success.
CREATE TABLE table1 (
id mediumint(9) NOT NULL AUTO_INCREMENT,
item_num mediumint(9) NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
CREATE TABLE table2 (
id mediumint(9) NOT NULL AUTO_INCREMENT,
oneid mediumint(9) NOT NULL,
item_desc varchar(16) NOT NULL,
PRIMARY KEY (id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
SELECT item_num, GROUP_CONCAT(item_desc) AS alldesc FROM table1 LEFT JOIN table2 ON table1.id = table2.oneid
So basically, there can be several item descripotions that may be NULL; they will be in no particular order either. So i am seeking a list with a placeholder when NULLs arise.
Does this work for you(use description as empty string when it is NULL)?
SELECT item_num,
REPLACE(GROUP_CONCAT(IFNULL(item_desc,' ')), ', ,', ',') AS alldesc
FROM table1
LEFT JOIN table2
ON table1.id = table2.oneid
you are missing GROUP BY in your query. chances are if you have multiple item_num, it will always return one row.
SELECT item_num, GROUP_CONCAT(item_desc) AS alldesc
FROM table1 LEFT JOIN table2
ON table1.id = table2.oneid
GROUP BY item_num
SELECT item_num,
REPLACE(
GROUP_CONCAT(
IFNULL(item_desc,'*!*') -- replace this with something not in a normal item_desc
ORDER BY if(item_desc is null, 1, 0) desc
), '*!*,','') AS alldesc
FROM table1
LEFT JOIN table2
ON table1.id = table2.oneid
GROUP BY item_num
Try the following query
SELECT item_num, GROUP_CONCAT(ISNULL(item_desc,'')) AS alldesc FROM table1 LEFT JOIN table2 ON table1.id = table2.oneid