Please take a look at the following table:
I am building a search engine which returns card_id values, based on search of category_id and value_id values.
To better explain the search mechanism, imagine that we are trying to find a car (card_id) by supplying information what part (value_id) the car should has in every category (category_id).
In example, we may want to find a car (card_id), where category "Fuel Type" (category_id) has a value "Diesel" (value_id), and category "Gearbox" (category_id) has a value "Manual" (value_id).
My problem is that my knowledge is not sufficient to build a query, which will returns card_ids which contains more than one pair of category_id and value_id.
For example, if I want to search a car with diesel engine, I could build a query like this:
SELECT card_id FROM cars WHERE category_id=1 AND value_id=2
where category_id = 1 is a category "Fuel Type" and value_id = 2 is "Diesel".
My question is, how can I build a query, which will look for more category-value pairs? For example, I want to look for diesel cars with manual gearbox.
Any help will be very appreciated. Thank you in advance.
You can do this using aggregation and a having clause:
SELECT card_id
FROM cars
GROUP BY card_id
HAVING SUM(category_id = 1 AND value_id = 2) > 0 AND
SUM(category_id = 3 and value_id = 43) > 0;
Each condition in the having clause counts the number of rows that match a given condition. You can add as many conditions as you like. The first, for instance, says that there is at least one row where the category is 1 and the value is 2.
SQL Fiddle
Another approach is to create a user defined function that takes a table of attribute/value pairs and returns a table of matching cars. This has the advantage of allowing an arbitrary number of attribute/value pairs without resorting to dynamic SQL.
--Declare a "sample" table for proof of concept, replace this with your real data table
DECLARE #T TABLE(PID int, Attr Int, Val int)
--Populate the data table
INSERT INTO #T(PID , Attr , Val) VALUES (1,1,1), (1,3,5),(1,7,9),(2,1,2),(2,3,5),(2,7,9),(3,1,1),(3,3,5), (3,7,9)
--Declare this as a User Defined Table Type, the function would take this as an input
DECLARE #C TABLE(Attr Int, Val int)
--This would be populated by the code that calls the function
INSERT INTO #C (Attr , Val) VALUES (1,1),(7,9)
--The function (or stored procedure) body begins here
--Get a list of IDs for which there is not a requested attribute that doesn't have a matching value for that ID
SELECT DISTINCT PID
FROM #T as T
WHERE NOT EXISTS (SELECT C.ATTR FROM #C as C
WHERE NOT EXISTS (SELECT * FROM #T as I
WHERE I.Attr = C.Attr and I.Val = C.Val and I.PID = T.PID ))
Related
I have tables
CREATE TABLE one (
op INT,
value INT
);
and
CREATE TABLE two (
tp INT,
value INT
);
Now I want to get all op values for which the set of values for the op contains all values for a given tp.
I would write this as:
SELECT op FROM one AS o1 WHERE (
(SELECT value FROM one AS o2 WHERE o1.op = o2.op)
CONTAINS ALL
(SELECT value FROM two WHERE tp=<specific-value>)
)
Unfortunately, I couldn't find such a CONTAINS ALL operator and nothing which would be close that.
Table one contains 50M entries, table two contains 1M entries. On average, there are 20 different values for a single op and tp.
Consider your tables name ops and tps.
SELECT
ops.op
FROM ops
INNER JOIN tps ON tps.value = ops.value
WHERE tps.tp = 1
GROUP BY ops.op
HAVING COUNT(DISTINCT ops.value) = (SELECT COUNT(DISTINCT tps.value) FROM tps WHERE tps.tp = 1); --- You can replace 1 with any tp value.
I have 3 tables
Category(Category_ID,Category_Name,Parent,Category_Tag)
News_Category(ID,News_ID,Category)
News(News_ID,Title,Article,News_Tags)
I want to fetch all Category_Tag from Category Table where News_ID=72. I am using following query in sql server:
DECLARE #cat varchar(100)
SET #cat=(select Category_Tag
from Category
where Category_ID in(
select Category
from News_Category
inner join Category on Category.Category_ID = News_Category.Category
where News_ID=72
)
)
but this query is not working for me it is showing error as:- Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
.Also i want to extract each single record from the above query to update News table column News_Tags.Suppose the News_Tags column in News table contains value "Tejpal" and the above query returning two values- National and Interbational. then the final value should be stored in News_Tags Column of News Table as Tejpal,National,International
Please help me here
The error here is pretty descriptive. You have declared a scalar variable which is designed to hold a single value, and you are trying to insert multiple rows into that variable.
You need to either do one of two things, either use a temporary table such as
DECLARE #Cat TABLE (CategoryTag VARCHAR(1000));
INSERT INTO #Car
SELECT C.Category_Tag
FROM Category C
INNER JOIN News_Category N ON N.Category = C.CategoryID
WHERE N.News_ID = 72
or you can concatenate the values to be a single string.
I think in this case you might be looking for the second option which would look something like this. This code is mostly psuedo taking from another script I have, but it should put you on the right track.
DECLARE #CategoryTags VARCHAR(1000);
SET #CategoryTags = (
SELECT STUFF((
SELECT ISNULL(Category_Tag, 'NullTag')
FROM Category C
INNER JOIN News_Category N ON N.Category = C.CategoryID
WHERE N.News_ID = N1.NewsID
FOR XML PATH (''),TYPE).value('(./text())[1]','VARCHAR (MAX)')
,1,0,'') [Categories]
FROM News_Category N1
WHERE N1.News_ID = 72
UPDATE News_Category
SET NewTags = NewTags + #CategoryTags
WHERE News_ID = 72
This might help you.
DECLARE #combinedString VARCHAR(MAX)
SELECT #combinedString = COALESCE(#combinedString + CHAR(13) + CHAR(10), '') + ISNULL(Category_Tag, 'NullTag')
FROM Category
select #combinedString
I have a table defined like the following...
CREATE table actions (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
end BOOLEAN,
type VARCHAR(15) NOT NULL,
subtype_a VARCHAR(15),
subtype_b VARCHAR(15),
);
I'm trying to query for the last end action of some type to happen on each unique (subtype_a, subtype_b) pair, similar to a group by (except SQLite doesn't say what row is guaranteed to be returned by a group by).
On an SQLite database of about 1MB, the query I have now can take upwards of two seconds, but I need to speed it up to take under a second (since this will be called frequently).
example query:
SELECT * FROM actions a_out
WHERE id =
(SELECT MAX(a_in.id) FROM actions a_in
WHERE a_out.subtype_a = a_in.subtype_a
AND a_out.subtype_b = a_in.subtype_b
AND a_in.status IS NOT NULL
AND a_in.type = "some_type");
If it helps, I know all the unique possibilities for a (subtype_a,subtype_b)
eg:
(a,1)
(a,2)
(b,3)
(b,4)
(b,5)
(b,6)
Beginning with version 3.7.11, SQLite guarantees which record is returned in a group:
Queries of the form: "SELECT max(x), y FROM table" returns the value of y on the same row that contains the maximum x value.
So greatest-n-per-group can be implemented in a much simpler way:
SELECT *, max(id)
FROM actions
WHERE type = 'some_type'
GROUP BY subtype_a, subtype_b
Is this any faster?
select * from actions where id in (select max(id) from actions where type="some_type" group by subtype_a, subtype_b);
This is the greatest-in-per-group problem that comes up frequently on StackOverflow.
Here's how I solve it:
SELECT a_out.* FROM actions a_out
LEFT OUTER JOIN actions a_in ON a_out.subtype_a = a_in.subtype_a
AND a_out.subtype_b = a_in.subtype_b
AND a_out.id < a_in.id
WHERE a_out.type = "some type" AND a_in.id IS NULL
If you have an index on (type, subtype_a, subtype_b, id) this should run very fast.
See also my answers to similar SQL questions:
Fetch the row which has the Max value for a column
Retrieving the last record in each group
SQL join: selecting the last records in a one-to-many relationship
Or this brilliant article by Jan Kneschke: Groupwise Max.
I used a Java method called 'containsAll()' to check if ArrayLists have common content.
Let's say I have a list A (one row), and several other lists in a MySQL table (in column 'name', row by row).
All lists consist of comma-separated Strings (at least one String in a list) - names or whatever.
Now, I want to check if all Strings in list A can be found in any of the rows in column 'name'.
The result set should show all the rows in 'name' that match, that includes rows/lists must have all Strings in list A, and can have additional Strings.
Example I
A: 'Mr.T'
____name_________________________________________
'Hannibal'
'Hannibal','Face','Murdock','Mr.T','Donald Duck'
'Face','Donald Duck'
'Superman','Chuck Norris','Mr.T'
_________________________________________________
Result set: 'Hannibal','Face','Murdock','Mr.T','Donald Duck' -AND-
'Superman',Chuck Norris','Mr.T'
Example II
A: 'Rocky', 'Mr.T', 'Apollo'
______name__________________________________________________
'Hannibal','Face','Murdock','Donald Duck','Superman','Mr.T'
'Rocky','Apollo','Ivan'
'Apollo', 'Superman','Hannibal','Rocky','Mr.T','Chuck Norris'
'Rocky','Mr.T','Apollo','Chuck Norris'
_____________________________________________________________
Result set: 'Apollo', 'Superman','Hannibal','Rocky','Mr.T','Chuck Norris' -AND-
'Rocky','Mr.T','Apollo','Cuck Norris'
I wonder if one can carry out those results using a MySQL query.
Thank you in advance.
It appears you want to do an array intersection, except your array is a single column. It can be done, but it will be slow, difficult to debug and will not leverage the power of relational databases. A better way would be to change your table schema to something like this:
Table groups
group_id int unsigned not null auto_increment primary key,
character_list text
Table members_in_group
group_id int unsigned not null,
group_member varchar(45) not null
Then you can query like this:
SELECT group_id, character_list
FROM groups g
JOIN members_in_groups m USING (group_id)
WHERE m.group_member IN ('Mr. T', ...);
The groups table is probably very like your current table. The members_in_groups table is the same data chopped up into easily searchable parts.
ETA given your comment, this should work if you can guarantee that each character_list contains only one instance of each character:
SELECT group_id,
SUM(CASE m.group_member IN ('Mr. T', 'Apollo', 'Rocky') THEN 1 ELSE 0 END) AS tally,
character_list
FROM groups g
JOIN members_in_groups m ON (g.group_id=m.group_id)
GROUP BY group_id
HAVING SUM(CASE m.group_member IN ('Mr. T', 'Apollo', 'Rocky') THEN 1 ELSE 0 END) = 3;
In this case the HAVING clause must equal 3 because there are 3 members in IN ('Mr. T', 'Apollo', 'Rocky').
I solved this issue by using the REGEXP function in MySQL:
SELECT * FROM `table` WHERE `column` REGEXP("(value1|value2|value3)");
SELECT * FROM tbl_name WHERE field_name LIKE '%\'Mr.T\'%'
I have 2 tables: tbl_taxclasses, tbl_taxclasses_regions
This is a one to many relationship, where the main record ID is classid.
I have a column inside the first table called regionscount
So, I create a Tax Class, in table 1. Then I add regions/states in table 2, assigning the classid to each region.
I perform a SELECT statement to count the regions with that same classid, and then I perform an UPDATE statement on tbl_taxclasses with that number. I update the regionscount column.
This means I'm writing 2 queries. Which is fine, but I was wondering if there was a way to do a SELECT statement inside the UPDATE statement, like this:
UPDATE `tbl_taxclasses` SET `regionscount` = [SELECT COUNT(regionsid) FROM `tbl_taxclasses_regions` WHERE classid = 1] WHERE classid = 1
I'm reaching here, since I'm not sure how robust MySQL is, but I do have the latest version, as of today. (5.5.15)
You could use a non-correlated subquery to do the work for you:
UPDATE
tbl_taxclasses c
INNER JOIN (
SELECT
COUNT(regionsid) AS n
FROM
tbl_taxclasses_regions
GROUP BY
classid
) r USING(classid)
SET
c.regionscount = r.n
WHERE
c.classid = 1
Turns out I was actually guessing right.
This works:
UPDATE `tbl_taxclasses`
SET `regionscount` = (
SELECT COUNT(regionsid) AS `num`
FROM `tbl_taxclasses_regions`
WHERE classid = 1)
WHERE classid = 1 LIMIT 1
I just needed to replace my brackets [] with parenthesis ().