Is there some way to JOIN on a resulting substring?
I've got a query that looks like this:
SELECT _atc_codes.se, _atc_codes.code, SUBSTR(_atc_codes.code, 1, 1)
FROM diagnoses
JOIN _atc_codes
ON (_atc_codes.id = diagnoses.atc_code)
Now I want to add an extra column to this query which should be SUBSTR(_atc_codes.code, 1, 1) joined to its corresponding _atc_codes.se, how do I do that?
This image shows incorrect results, the 4th column should read "Matsmältningsorgan och ämnesomsättning" (corresponding cell in _atc_codes.se).
SQL Fidde: http://www.sqlfiddle.com/#!2/6695d
Can you try (this example uses MySQL user defined variables, it is important for this query that MySQL knows to use a value outside from subquery for comparision)
SELECT _atc_codes.se, _atc_codes.code,
#code_substr:=SUBSTR(_atc_codes.code, 1, 1) AS code_substr,
(
SELECT se
FROM _atc_codes
WHERE code=#code_substr
LIMIT 1
) AS code_substr_se
FROM diagnoses
JOIN _atc_codes ON _atc_codes.id = diagnoses.atc_code
Or (this example assigns table alias to outer table which is used in subquery because you are using a table twice and MySQL does not know which table to reference in SUBSTR in subquery):
SELECT outer_codes .se, outer_codes .code,
SUBSTR(outer_codes .code, 1, 1) AS code_substr,
(
SELECT se
FROM _atc_codes
WHERE code=SUBSTR(outer_codes.code, 1, 1)
LIMIT 1
) AS code_substr_se
FROM diagnoses
JOIN _atc_codes AS outer_codes ON outer_codes.id = diagnoses.atc_code
A third way would be adding a second JOIN and then group resultset like
SELECT _atc_codes_1st.se, _atc_codes_1st.code,
SUBSTR(_atc_codes_1st.code, 1, 1) AS code_substr,
MAX(_atc_codes_2nd.se) AS code_substr_se
FROM diagnoses
JOIN _atc_codes AS _atc_codes_1st ON _atc_codes_1st.id = diagnoses.atc_code
JOIN _atc_codes AS _atc_codes_2nd ON _atc_codes_2nd.code = SUBSTR(_atc_codes_1st.code, 1, 1)
GROUP BY _atc_codes_1st.se, _atc_codes_1st.code,code_substr
Deciding which variant to use, it would be the best to add a EXPLAIN to your query to show execution plan. Good luck.
Related
In my query I use join table category_attributes. Let's assume we have such rows:
category_id|attribute_id
1|1
1|2
1|3
I want to have the query which suites the two following needs. I have a variable (php) of allowed attribute_id's. If the array is subset of attribute_id then category_id should be selected, if not - no results.
First case:
select * from category_attributes where (1,2,3,4) in category_attributes.attribute_id
should give no results.
Second case
select * from category_attributes where (1,2,3) in category_attributes.attribute_id
should give all three rows (see dummy rows at the beginning).
So I would like to have reverse side of what standard SQL in does.
Solution
Step 1: Group the data by the field you want to check.
Step 2: Left join the list of required values with the records obtained in the previous step.
Step 3: Now we have a list with required values and corresponding values from the table. The second column will be equal to required value if it exist in the table and NULL otherwise.
Count null values in the right column. If it is equal to 0, then it means table contains all the required values. In that case return all records from the table. Otherwise there must be at least one required value is missing in the table. So, return no records.
Sample
Table "Data":
Required values:
10, 20, 50
Query:
SELECT *
FROM Data
WHERE (SELECT Count(*)
FROM (SELECT D.value
FROM (SELECT 10 AS value
UNION
SELECT 20 AS value
UNION
SELECT 50 AS value) T
LEFT JOIN (SELECT value
FROM Data
GROUP BY value) D
ON ( T.value = D.value )) J
WHERE value IS NULL) = 0;
You can use group by and having:
select ca.category_id
from category_attributes ca
where ca.attribute_id in (1, 2, 3, 4)
group by ca.category_id
having count(*) = 4; -- "4" is the size of the list
This assumes that the table has no duplicates (which is typical for attribute mapping tables). If that is a possibility, use:
having count(distinct ca.attribute_id) = 4
You can aggregate attribute_id into array and compare two array from php.
SELECT category_id FROM
(select category_id, group_concat(attribute_id) as attributes from category_attributes
order by attribute_id) t WHERE t.attributes = (1, 2, 3);
But you need to find another way to compare arrays or make sure that array is always sorted.
Is there any way to do that in a single query? Or do I have to manage it externally? It is not a JOIN of any kind.
SELECT
IF (
(SELECT indicator FROM configuration_table) = 1,
(SELECT series_id FROM series_table LIMIT 1),
''
) as if_exp
FROM
series_table
This executes but returns the first ID over and over, and if I take out the LIMIT 1, it doesn't work as it expects only one result. But what I need is that, if this condition is met:
(SELECT indicator FROM configuration_table) = 1,
Then I need all this data returned:
SELECT series_id, series_code, series_name FROM series_table
Is it possible somehow? Should I be doing two queries and managing the data from php? Thank you very much.
The easiest way would be:
IF ((SELECT indicator FROM configuration_table) = 1) THEN
SELECT series_id, series_code, series_name FROM series_table
END IF
You did not show us what to do, when the condition is false. We do not know the relationship between configuration_table and series_table, so we can't find a way to make it in a single query.
I have copied this answer from IF Condition Perform Query, Else Perform Other Query this answer.
SELECT CASE WHEN ( (SELECT indicator FROM configuration_table) = 1 )
THEN
SELECT series_id, series_code, series_name FROM series_table
ELSE
<QUERY B>
END
Here Query B should replaced by your desired query.
I have this query:
select *
from transaction_batch
where id IN
(
select MAX(id) as id
from transaction_batch
where status_id IN (1,2)
group by status_id
);
The inner query runs very fast (less than 0.1 seconds) to get two ID's, one for status 1, one for status 2, then it selects based on primary key so it is indexed. The explain query says that it's searching 135k rows using where only, and I cannot for the life of me figure out why this is so slow.
The inner query is run seperatly for every row of your table over and over again.
As there is no reference to the outer query in the inner query, I suggest you split those two queries and just insert the results of the inner query in the WHERE clause.
select b.*
from transaction_batch b
inner join (
select max(id) as id
from transaction_batch
where status_id in (1, 2)
group by status_id
) bm on b.id = bm.id
my first post here.. sorry about the lack of formatting
I had a performance problem shown below:
90sec: WHERE [Column] LIKE (Select [Value] From [Table]) //Dynamic, slow
1sec: WHERE [Column] LIKE ('A','B','C') //Hardcoded, fast
1sec: WHERE #CSV like CONCAT('%',[Column],'%') //Solution, below
I had tried joining rather than subquerying.
I had also tried a hardcoded CTE.
I had lastly tried a temp table.
None of these standard options worked, and I was not willing to dosp_execute option.
The only solution that worked as:
DECLARE #CSV nvarchar(max) = Select STRING_AGG([Value],',') From [Table];
// This yields #CSV = 'A,B,C'
...
WHERE #CSV LIKE CONCAT('%',[Column],'%')
I have a client database that is in a pretty poor state, but I have to work with it. We have a set of product id's and each id is made up of the attributes of the product. What i'd like to do is select those id's that match the first 4 or 5 characters and have them in a drop down box as variants of a selected product.
I've tried 'group by subtr(0,6)', but this seems to return only the first match in the group. What I need is to have separate queries, that ONLY return ALL the individual rows that have a matching first x characters.
So, for example, I want to have queries that will return:
12345xx
12345yy
12345zz
and then another that will return:
56789xx
56789yy
56789zz
This following query would return only:
12345xx
and then
56789yy for example
select tbl_item.Description, tbl_item.`Description 2`, tbl_item.`Inventory Posting Group`, tbl_item.No_, tbl_item.`Web Description`, tbl_item.`Web Headers`, tbl_item.`Unit Price`, tbl_item.`Sales (Qty_)`, tbl_item.`Product Group Code`
from tbl_item
where tbl_item.`Product Group Code` = ':shopprodid' and tbl_item.`Vendor No_` = '$vendor_no'
group by substr(0,6)
The solution is to put the substr and LIKE in the WHERE clause (no need to group anything). And btw, in MySql the substring starts at index 1, not 0.
select *
from tbl_item
where substr(tbl_item.`Product Group Code`, 1, 6) LIKE ':shopprodid%'
What do you mean by substr(0,6)? the correct syntax is
SUBSTR( str, pos, len )
I suppose you mean:
SUBSTR( tbl_item.id, 0, 6 )
correct that.
And for the results you want you don't need the substr at all, use a query like this:
SELECT * from tbl_item WHERE tbl_item.id LIKE '12345%'
Try something like the following;
SELECT * FROM tbl_item WHERE SUBSTR([<Column name>], 1, 6) = '<Your ID prefix>' ORDER BY [<Column name>]
SELECT *
FROM `seriallog` WHERE `lastevent` IN
(SELECT GROUP_CONCAT(CONCAT(' \'', `eventid`, '\''))
FROM `permissions` WHERE `permissions`.`userpkid` = 1)
You are likely trying to compare an int (lastevent) to a concatenated string (the result of your subquery).
It is hard to tell without more info, but this probably what you want to do:
select *
from seriallog sl
inner join permissions p on sl.lastevent = p.eventid
where p.userpkid = 1
The way you've written it, the inner query will return a string like "1, 2, 3" so only rows in seriallog with a lastevent column of "1, 2, 3" exactly will match.
You're presumably interested in matching any row whose value is in a SET. That's what SQL is designed to do; you don't have to apply any special engineering. The following would work:
SELECT *
FROM seriallog WHERE lastevent IN
(SELECT eventid FROM permissions WHERE permissions.userpkid = 1)
However, it would be preferable to instead write:
SELECT *
FROM seriallog
WHERE EXISTS (SELECT 1 FROM permissions WHERE eventid = seriallog.lastevent AND permissions.userpkid = 1)
This allows MySQL to treat the query more like a JOIN, and presumably execute it more efficiently.
This is most optimized format for MySQL. In general MySQL does not love Sub-Queries.
SELECT *
FROM seriallog sl
join permissions p on sl.lastevent=p.eventid
WHERE permissions.userpkid = 1