Merge the query results,optimize? - mysql

mysql,two tables:test(one) and review(many).
My goal:from review for one in the corresponding number
SELECT t.ID,t.TITLE,COALESCE(COUNT(r.ID),0) `count`
FROM `test` t
LEFT OUTER JOIN review r
ON t.ID = r.REVIEW_OBJ_ID
WHERE r.REVIEW_TYPE = '4'
ORDER BY `count` DESC;
Output:
ID TITLE count
402884f657e0a6d20157e0a82cc90000 brother 2
test table(A small portion of the data)
SELECT t.ID,t.TITLE
FROM `test` t;
Output:
ID TITLE
40284c8157ad8e7d0157ad8f86880000 1234567890123456789012345
402884f657e0a6d20157e0a82cc90000 brother
402884f657e0a6d20157e11967a20036 fg
402884f657e51eff0157e54cd8610004 AAA
402884f657e652fb0157e65642750000 BBB
0000000057f4dc900157f4ea9edd0000 VVV
00000000580065c5015800746d750000 CCC
00000000580065c501581d9f04f0000b TTT
And I want get this:
ID TITLE count
402884f657e0a6d20157e0a82cc90000 brother 2
402884f657e652fb0157e65642750000 BBB 0
00000000580065c501581d9f04f0000b TTT 0
402884f657e0a6d20157e11967a20036 fg 0
0000000057f4dc900157f4ea9edd0000 VVV 0
40284c8157ad8e7d0157ad8f86880000 1234567890123456789012345 0
402884f657e51eff0157e54cd8610004 AAA 0
00000000580065c5015800746d750000 CCC 0
so,I tried this and it worked:
SELECT t.ID,t.TITLE, COALESCE(r.c,0) `count`
FROM `test` t
LEFT OUTER JOIN
(
SELECT r.REVIEW_OBJ_ID obj_id, COUNT(r.ID) c
FROM review r,`test` t
WHERE r.REVIEW_TYPE = '4'
AND t.ID = r.REVIEW_OBJ_ID
) r ON r.obj_id = t.ID
ORDER BY `count` DESC;
But I have two questions:
It feels I can use one-time select to found out result,but I use two-times select.Can I optimize it?
Add a count(redundant) in the test table fields, whether it is a better choice.
/REVIEW_TYPE and REVIEW_OBJ_ID decide which object is reviewed,just like I use "REVIEW_TYPE='4'" to contact the test table/
drop table if exists user_doctor_review;
create table review
(
ID varchar(64) not null,
USER_ID varchar(64),
DOCTOR_ID varchar(64),
REVIEW_TYPE varchar(1),
REVIEW_OBJ_ID varchar(64),
SERVICE_SCORE int(6),
REVIEW_CONTENT varchar(600),
REVIEW_TIME datetime,
POID varchar(64),
IS_ANONYMITY varchar(1),
CHECKED_STATUS varchar(1),
STATUS varchar(1),
REPLY_CONTENT varchar(600),
REPLY_TIME datetime,
DOCTOR_IS_READ varchar(1),
primary key (ID)
);

Yes you can do it with one SELECT statement with this query
SELECT test.ID, test.TITLE, count(review.ID) as count from test
left join review on test.ID = review.REVIEW_OBJ_ID
where review.REVIEW_TYPE = 4 or review.ID is null
group by test.ID
I have created a SQL Fiddle here: http://sqlfiddle.com/#!9/c6a178/15
Explanation:
The key points are:
or review.ID is null
because it will make the query list the tests with no reviews, and
group by test.ID
this will get the correct review counts related to the test.
Results:
ID TITLE count
-------------------------------- ------------------------- -----
0000000057f4dc900157f4ea9edd0000 VVV 0
00000000580065c5015800746d750000 CCC 0
00000000580065c501581d9f04f0000b TTT 1
40284c8157ad8e7d0157ad8f86880000 1234567890123456789012345 0
402884f657e0a6d20157e0a82cc90000 brother 2
402884f657e0a6d20157e11967a20036 fg 0
402884f657e51eff0157e54cd8610004 AAA 0
402884f657e652fb0157e65642750000 BBB 0

Related

Select distinct one column by conditions from the other 2 columns (how to pair them?)

I need to get the id_user by values given in other columns. My table look like this:
id(AI,PK) id_user attr_name attr_value
----------------------------------------------------
1 1 hair brown
2 1 eyes green
3 2 hair blond
4 1 age 40
5 1 sex male
6 2 eyes green
7 2 age 40
8 2 sex male
When I try a query like this:
select distinct id_user where (attr_name='hair' and attr_value='blond') or (attr_name='eyes' and attr_value='green')
I will obviously get id_user=1 and 2, because both of them have green eyes.
If I change "or" to "and" it seems that the query does not work at all. But I need "and" because 2 (or even more, I shortened my example) conditions must be met, to get the specified id_user:
select distinct id_user where (attr_name='hair' and attr_value='blond') and (attr_name='eyes' and attr_value='green')
How to "pair" those 2 brackets, so I will get only a user where both conditions met: green eyes and blond hair?
Use post aggregate filtering with HAVING.
WHERE filters rows, HAVING with aggregate functions filters groups.
SELECT id_user FROM t
GROUP BY id_user
HAVING SUM(attr_name='hair')>0 AND SUM( attr_value='blond') >0
AND SUM(attr_name='eyes')>0 AND SUM( attr_value='green') >0
Another approach is.
SELECT `u1`.`id_user`,
IF(`u1`.`attr_name` RLIKE 'hair' AND `u1`.`attr_value` = 'blond',1,0) AS `blond`,
IF(`u2`.`attr_name` RLIKE 'eyes' AND `u2`.`attr_value` = 'green',1,0) AS `green`
FROM `users` `u1`
INNER JOIN `users` `u2`
ON `u1`.`id_user`=`u2`.`id_user`
HAVING `green`+`blond` = 2;
OR
SELECT `u1`.`id_user`
FROM `users` `u1`
INNER JOIN `users` `u2`
ON `u1`.`id_user`=`u2`.`id_user`
WHERE IF(`u1`.`attr_name` RLIKE 'hair' AND `u1`.`attr_value` = 'blond',1,0) + IF(`u2`.`attr_name` RLIKE 'eyes' AND `u2`.`attr_value` = 'green',1,0) = 2;
The result is
id_user blond green
2 1 1
To re-create the example use:
create table `users` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`id_user` INT,
`attr_name` VARCHAR(255) NOT NULL,
`attr_value` VARCHAR(255) NOT NULL
) ENGINE=INNODB;
INSERT INTO `users` (`id`,`id_user`,`attr_name`,`attr_value`) VALUES (1,1,'hair','blond'),(2,1,'eyes','blue'),(3,2,'hair','blond'),(4,2,'eyes','green'),(5,2,'sex','male'),(6,2,'age','42'),(7,1,'sex','female'),(8,1,'age','39');
Try it on SQL Fiddle

MySQL, Remove duplicate

I need to remove duplicates from my table but MySQL is not working properly
Create table emp
( empID INT(5) PRIMARY KEY,
Pref01 int(1),
Pref02 int(1),
Pref03 int(1),
Pref04 int(1))
empID, Pref01, Pref02, Pref03, Pref04
=====================================
00011 1 2 0 0
00011 1 3 0 0
00022 1 1 0 0
00022 0 3 0 0
I need to keep these records
00011 1 3 0 0
00022 0 3 0 0
also I need to keep any record with all pref null value
this is my sql:
select empID
FROM emp
where max(Pref01) or max (Pref02) or max(Pref03) or max(Pref04)
or Pref01 is null or Pref02 is null or Pref03 is null or Pref04 is null
Your problem is quite complicated, and with given information I have to make an assumption to answer it..
Assume there is no record with same empID have same maximal pref number...
SELECT A.*
FROM emp AS A
INNER JOIN (
SELECT empID, MAX(GREATEST(Pref01, Pref02, Pref03, Pref04)) AS MaxPref
FROM emp GROUP BY empID
) AS B ON A.empID = B.empID
WHERE
(Pref01 = MaxPref OR Pref02 = MaxPref OR Pref03 = MaxPref OR Pref04 = MaxPref)
OR
(Pref01 IS NULL AND Pref02 IS NULL AND Pref03 IS NULL AND Pref04 IS NULL)
If the assumption is not correct then the code will still show duplicate for empID with same max pref number more than one.. to fix it is much more complicated than this code..
You can get your data using GROUP BY and GREATEST:
SELECT empID, max(GREATEST(Pref01, Pref02, Pref03, Pref04)) FROM emp GROUP BY empID
you can use this to find rows directly in emp table using exists or in

Validating presence of value(s) in a (sub)table and return a "boolean" result

I want to create a query in MySQL, on an order table and verify if it has a booking id, if it does not have a booking_id it should available on all relations in the invoice table.
I want the value returned to be a boolean in a single field.
Taken the example given, in
Case of id #1 I expect an immediate true, because it's available
Case of id #2 I expect an "delayed" false from the invoice table as not all related invoices have an booking_id, it should only return true if invoice id #3 actually has an booking id, meaning all invoices have an booking_id when the order does not.
I've tried several ways but still failed and don't even know what the best way to tackle this is.
Thanks for your input in advance!
Table order:
|----+------------+
| id | booking_id |
|----+------------+
| 1 | 123 |
| 2 | NULL |
|----+------------+
Table invoice:
+----+----------+------------+
| id | order_id | booking_id |
+----+----------+------------+
| 1 | 1 | 123 |
| 2 | 2 | 124 |
| 3 | 2 | NULL |
+----+----------+------------+
Schema
CREATE TABLE IF NOT EXISTS `invoice` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`order_id` int(11) NOT NULL,
`booking_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
)
CREATE TABLE IF NOT EXISTS `order` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`booking_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
If I understand you correctly, this is the base query for your request:
SELECT
O.id
, SUM(CASE WHEN I.booking_id IS NOT NULL THEN 1 ELSE 0 END) AS booked_count
, COUNT(1) AS total_count
, CASE WHEN SUM(CASE WHEN I.booking_id IS NOT NULL THEN 1 ELSE 0 END) = COUNT(1) THEN 1 ELSE 0 END AS has_all_bookings
FROM
`order` O
LEFT JOIN invoice I
ON O.id = I.order_id
GROUP BY
O.id
If you want to check if there is no record in the invoice table add the COUNT(1) to the last CASE statement as an additional condition (COUNT(1) = 0)
Fiddle Demo
I have not understood how the logic works out when the order is booked but some of the invoices are not. I'll presume either is good for a true value (OR logic). I'd avoid COUNT and GROUP BY and go for a SUBSELECT, which works fine in MySQL (I'm using and old 5.1.73-1 version).
This query gives you both values in distinct columns:
SELECT o.*
, (booking_id IS NOT NULL) AS order_booked
, (NOT EXISTS (SELECT id FROM `invoice` WHERE order_id=o.id AND booking_id IS NULL)) AS invoices_all_booked
FROM `order` o
Of course you can combine the values:
SELECT o.*
, (booking_id IS NOT NULL OR NOT EXISTS (SELECT id FROM `invoice` WHERE order_id=o.id AND booking_id IS NULL)) AS booked
FROM `order` o
Here you go, create a view that does it
create view booked_view as
select `order`.id as order_id
,
case when booking_id > 0 then true
when exists (SELECT id FROM invoice WHERE order_id=`order`.id AND invoice.booking_id IS NULL) then true
else false
end as booked
from `order` ;
Then just join your view to the order table and you will have your boolean column 'booked'
select o.id, booked from `order` o
join booked_view on (o.id = booked_view.order_id)

Attempting to concatenate multiple rows.. unexpected results

Issue:
I'm trying to return a list of contacts with a column containing all the parent item names concatenated together. Please don't bother telling me this violates 1st normal form, I already know. I've been trying to get this working for so long that it is probably some stupid mistake, but I need some help at this point.
Query:
SELECT C.ID, C.SequenceNumber, C.ContactType, C.GUID, C.ContactCategory, MAP.Parents, C.LastName, C.FirstName, C.CompanyName, C.Title, C.Email, C.Phone, C.MobilePhone, C.Fax, C.Comments, C.StandardConfirmation, C.Active FROM
(
SELECT ContactTypeMapping.Contact_GUID, STUFF( ( SELECT ','+ [NAME]
FROM ContactParents a
WHERE a.GUID =b.GUID
FOR XML PATH('')),1 ,1, '') Parents
FROM ContactParents b, ContactTypeMapping
WHERE ContactTypeMapping.Parent_GUID=b.GUID
) MAP
INNER JOIN
(
SELECT Contact.ID, Contact.GUID, Contact.SequenceNumber,
Contact.ContactType, Contact.ContactCategory, Contact.LastName, Contact.FirstName,
Contact.CompanyName, Contact.Title, Contact.Email, Contact.Phone,
Contact.MobilePhone, Contact.Fax, Contact.Comments, Contact.StandardConfirmation, Contact.Active
FROM Contact
)C
ON (MAP.Contact_GUID=C.GUID)
Current results:
ID SequenceNumber ContactType GUID ContactCategory Parents LastName FirstName CompanyName Title Email Phone MobilePhone Fax Comments StandardConfirmation Active
15 4 2 95A566D0-DB83-4853-9CB7-E6CF3B1FF814 0 AParent Beard Kirk NULL Business Kirk_Beard#someplace.com 913-906-3333 NULL (913) 906-3434 NULL 0 1
15 4 2 95A566D0-DB83-4853-9CB7-E6CF3B1FF814 0 AnotherParent Beard Kirk NULL Business Kirk_Beard#someplace.com 913-906-3333 NULL (913) 906-3434 NULL 0 1
Desired results:
15 4 2 95A566D0-DB83-4853-9CB7-E6CF3B1FF814 0 AParent,AnotherParent Beard Kirk NULL Business Kirk_Beard#someplace.com 913-906-3333 NULL (913) 906-3434 NULL 0 1
Data:
ContactParents Table:
GUID NAME TYPE
C40A6F7E-F760-48D6-8BAF-E55EC7DC900D AParent Place
A651A0A3-5A50-45F1-AB4B-2B7FDCE9734C AnotherParent Place
ContactTypeMapping Table:
Contact_GUID Parent_GUID ParentTable
95A566D0-DB83-4853-9CB7-E6CF3B1FF814 C40A6F7E-F760-48D6-8BAF-E55EC7DC900D Place
95A566D0-DB83-4853-9CB7-E6CF3B1FF814 A651A0A3-5A50-45F1-AB4B-2B7FDCE9734C Place
Contact Table:
ID GUID SequenceNumber ContactType LastName FirstName Title Email Phone MobilePhone Fax Comments Active ContactCategory CompanyName StandardConfirmation
15 95A566D0-DB83-4853-9CB7-E6CF3B1FF814 4 2 Beard Kirk Business Kirk_Beard#someplace.com 913-906-3333 NULL (913) 906-3434 NULL 1 0 NULL 0
;WITH cp AS -- contact parents - initial join
(
SELECT cp.NAME, ctm.Parent_GUID
FROM ContactParents AS cp
INNER JOIN ContactTypeMapping AS ctm
ON cp.GUID = ctm.Contact_GUID
),
cm AS -- contact mapping with concatenated values
(
SELECT Parent_GUID, Parents = (
SELECT STUFF ((SELECT ','+ [NAME] FROM cp AS cp2
WHERE cp2.Parent_GUID = cp.Parent_GUID
FOR XML PATH(''),
TYPE).value(N'./text()[1]',N'nvarchar(max)'),1 ,1, '')
)
FROM cp GROUP BY Parent_GUID
)
SELECT c.[GUID], cm.Parents --, other columns from c
FROM Contact AS c
INNER JOIN cm
ON c.[GUID] = cm.Parent_GUID;
To demonstrate how I verified that this query returns the right results, here are the table variables I created locally, how I populated them, and a slightly different query that references the table variables:
DECLARE #ContactParents TABLE
(
[GUID] UNIQUEIDENTIFIER,
NAME VARCHAR(32),
[TYPE] VARCHAR(32)
);
INSERT #ContactParents VALUES
('C40A6F7E-F760-48D6-8BAF-E55EC7DC900D','AParent','Place'),
('A651A0A3-5A50-45F1-AB4B-2B7FDCE9734C','AnotherParent','Place');
DECLARE #ContactTypeMapping TABLE
(
Contact_GUID UNIQUEIDENTIFIER,
Parent_GUID UNIQUEIDENTIFIER,
ParentTable VARCHAR(32)
);
INSERT #ContactTypeMapping VALUES
('A651A0A3-5A50-45F1-AB4B-2B7FDCE9734C','95A566D0-DB83-4853-9CB7-E6CF3B1FF814','Place'),
('C40A6F7E-F760-48D6-8BAF-E55EC7DC900D','95A566D0-DB83-4853-9CB7-E6CF3B1FF814','Place');
DECLARE #Contact TABLE
(
ID INT,
[GUID] UNIQUEIDENTIFIER,
LastName VARCHAR(32),
FirstName VARCHAR(32)
--, other columns...
);
INSERT #Contact VALUES
(15, '95A566D0-DB83-4853-9CB7-E6CF3B1FF814', 'Beard', 'Kirk');
;WITH cp AS
(
SELECT cp.NAME, ctm.Parent_GUID
FROM #ContactParents AS cp
INNER JOIN #ContactTypeMapping AS ctm
ON cp.GUID = ctm.Contact_GUID
),
cm AS
(
SELECT Parent_GUID, Parents = (
SELECT STUFF ((SELECT ','+ [NAME] FROM cp AS cp2
WHERE cp2.Parent_GUID = cp.Parent_GUID
FOR XML PATH('')),1 ,1, '')
)
FROM cp
GROUP BY Parent_GUID
)
SELECT c.[GUID], cm.Parents --, other columns from c
FROM #Contact AS c
INNER JOIN cm
ON c.[GUID] = cm.Parent_GUID;
Results
GUID Parents
95A566D0-DB83-4853-9CB7-E6CF3B1FF814 AParent,AnotherParent

SELECT CASE with Alias in SQL Server 2008

I have a stored Procedure that I cant get to work:
ALTER PROCEDURE GetBrands
#sortColumn INT
AS
SELECT DISTINCT(tblBrand.BrandID),
tblBrandinCategory.CategoryID,
tblBrand.BrandName AS Brand,
AVG(tblReview.Grade) AS AverageGrade,
COUNT(tblReview.ReviewID) AS Review
FROM tblBrand LEFT JOIN
tblBrandinCategory ON tblBrand.BrandID = tblBrandinCategory.BrandID LEFT JOIN
tblReview ON tblBrand.BrandID = tblReview.BrandID
GROUP BY tblBrand.BrandID, tblBrandinCategory.CategoryID, tblBrand.BrandName
ORDER BY
CASE
WHEN #sortColumn = 1 THEN Brand
WHEN #sortColumn = 2 THEN Review
WHEN #sortColumn = 4 THEN AverageGrade
ELSE Brand
END
The result that I want to have is a list with brands, that only will be displayed once. The tblBrandInCategory messes that up for me.
tblBrand
BrandID BrandName
1 Nike
2 Adidas
3 Puma
tblCategory
CategoryID CategoryName
1 Shoes
2 Shorts
3 Pants
tblBrandInCategory
CategoryID BrandID
1 1
2 1
3 1
tblReview
ReviewID Grade BrandID
1 5 1
2 9 1
3 2 1
I get the result multiplyed with three becouse BrandID 1 exist 3 times in tblBrandInCategoiry.
Another problem is that in the ORDER BY CASE I get errors that AverageGrade is not recognizable, but tblReview.Grade is fine, but I want to order it on the Average Grade.
Another problem is that in the ORDER BY CASE I get errors that
AverageGrade is not recognizable, but tblReview.Grade is fine, but I
want to order it on the Average Grade.
AverageGrade can not be used as a column to sort until your main query is the Sub Query....
I get the result multiplyed with three becouse BrandID 1 exist 3 times
in tblBrandInCategoiry.
create table #tblBrand
(
BrandID int,
BrandName varchar(10)
)
create table #tblCategory
(
CategoryID int,
CategoryName varchar(10)
)
create table #tblBrandInCategory
(
CategoryID int,
BrandID int
)
create table #tblReview
(
ReviewID int,
Grade int,
BrandID int
)
insert into #tblBrand(BrandID, BrandName)values(1, 'Nike')
insert into #tblBrand(BrandID, BrandName)values(2, 'Adidas')
insert into #tblBrand(BrandID, BrandName)values(3, 'Puma')
insert into #tblCategory(CategoryID, CategoryName)values(1, 'Shoes')
insert into #tblCategory(CategoryID, CategoryName)values(2, 'Shorts')
insert into #tblCategory(CategoryID, CategoryName)values(3, 'Pants')
insert into #tblBrandInCategory(CategoryID, BrandID)values(1, 1)
insert into #tblBrandInCategory(CategoryID, BrandID)values(2, 1)
insert into #tblBrandInCategory(CategoryID, BrandID)values(3, 1)
insert into #tblReview(ReviewID, Grade, BrandID)values(1, 5, 1)
insert into #tblReview(ReviewID, Grade, BrandID)values(2, 5, 9)
insert into #tblReview(ReviewID, Grade, BrandID)values(3, 2, 1)
Select BrandID, Brand, AverageGrade, Review
From
(
SELECT DISTINCT(#tblBrand.BrandID),
--#tblBrandinCategory.CategoryID,
#tblBrand.BrandName AS Brand,
AVG(#tblReview.Grade) AS AverageGrade,
COUNT(#tblReview.ReviewID) AS Review
FROM #tblBrand
LEFT JOIN #tblBrandinCategory ON #tblBrand.BrandID = #tblBrandinCategory.BrandID
LEFT JOIN #tblReview ON #tblBrand.BrandID = #tblReview.BrandID
GROUP BY #tblBrand.BrandID, #tblBrandinCategory.CategoryID, #tblBrand.BrandName
)K
ORDER BY
CASE
WHEN #sortColumn = 1 THEN Brand
WHEN #sortColumn = 2 THEN Review
WHEN #sortColumn = 4 THEN AverageGrade
ELSE Brand
drop table #tblBrand
drop table #tblCategory
drop table #tblBrandInCategory
drop table #tblReview
Final Resultset
Just edit the SELECT to leave off tblBrandInCategory results:
SELECT tblBrand.BrandID,
tblBrand.BrandName AS Brand,
AVG(tblReview.Grade) AS AverageGrade,
COUNT(tblReview.ReviewID) AS Review
FROM tblBrand
--tblBrandinCategory isn't even needed for what you're doing
--LEFT JOIN tblBrandinCategory ON tblBrand.BrandID = tblBrandinCategory.BrandID
LEFT JOIN tblReview ON tblBrand.BrandID = tblReview.BrandID
GROUP BY tblBrand.BrandID, tblBrand.BrandName
This would need to be edited further to account for null joins, where a grade does not exist (maybe with AVG(ISNULL(tblReview.Grade, 0))). Depends on your requirements.