WHere clause taking much longer time - mysql

HI i am using this query below its taking hell lot of a time around like 5 mins .. its crazy .. any better way ? or reason fr such long time .. even cutting it into smaller query n getting the value n then finding the common values is much much faster then this.
SELECT Product_ItemID
FROM Product_ItemProperties
LEFT JOIN Product_Items USING (Product_ItemID)
WHERE
Product_CatalogueID = 'xx' AND
Field = 'brandname' AND
MATCH (Value) AGAINST ('xx' IN BOOLEAN MODE) AND
Product_ItemID IN (Select Product_ItemID
FROM Product_ItemProperties
Where Field = 'xx' AND
Match (Value) AGAINST ('xx' IN BOOLEAN MODE)
);

i dont know why in first where clause you are making Field = 'brandname' and in second inner where clause you are filtering by Field = 'xx' . anyway you are double selecting Product_ItemProperties while you should use it once.
try this:
SELECT
Product_ItemID
FROM
Product_ItemProperties
LEFT JOIN
Product_Items USING (Product_ItemID)
WHERE
Product_CatalogueID = 'xx'
AND Field = 'brandname'
AND MATCH (Value) AGAINST ('xx' IN BOOLEAN MODE)
AND Field = 'xx';

It looks like your itemProperties table has multiple entries for the same "Product_ItemID", and you are looking for something that is both "BrandX", AND has some "OtherProperty" of a different value. To handle this, you can use that table TWICE (or more if more property values you are interested in... I would suggest having an index on your Product_ItemProperties table by (Product_ItemID, Field, Value) to be best optimization of the query.
For example, you are looking for a Car Brand "X", and secondly, it is a "Blue" car (not considering the catalog component).
Also, notice in this query, I give simplified aliases, and qualify each field with the alias.field so there is no ambiguity which field is coming from where.
The outer WHERE clause is your first criteria, only get those items that have a minimum of the brand name field, and it matches the value you are expecting... From those, join again to the product item properties table, but for the "other" field value you are interested in, and its value.
I was unsure where the cataglog component was, but I suspect that's from the product table and should have ITs alias adjusted.
SELECT
ByBrand.Product_ItemID,
P.NameOfProduct, (just an example to get this too)
FROM
Product_ItemProperties ByBrand
JOIN Product_Items P
ON ByBrand.Product_ItemID = P.Product_ItemID
JOIN Product_ItemProperties ByOtherField
ON ByBrand.Product_ItemID = ByOtherField.Product_ItemID
AND ByOtherField.Field = 'otherPropertyInterestedIn'
AND MATCH (ByOtherField.Value) against ( 'otherValueLookingFor' IN BOOLEAN MODE )
WHERE
ByBrand.Product_CatalogueID = 'someCatalogID' (or is this from product_items table)
AND ByBrand.Field = 'brandname'
AND MATCH (ByBrand.Value) against ( 'brandValueLookingFor' IN BOOLEAN MODE )

Related

SELECT statement inside a CASE statement in SNOWFLAKE

I have a query where i have "TEST"."TABLE" LEFT JOINED to PUBLIC."SchemaKey". Now in my final select statement i have a case statement where i check if c."Type" = 'FOREIGN' then i want to grab a value from another table but the table name value i am using in that select statement is coming from the left joined table column value. I've tried multiple ways to get to work but i keep getting an error, although if i hard code the table name it seems to work. i need the table name to come from c."FullParentTableName". Is what i am trying to achieve possible in snowflake and is there a way to make this work ? any help would be appreciated !
SELECT
c."ParentColumn",
c."FullParentTableName",
a."new_value",
a."column_name"
CASE WHEN c."Type" = 'FOREIGN' THEN (SELECT "Name" FROM TABLE(c."FullParentTableName") WHERE "Id" = 'SOME_ID') ELSE null END "TestColumn" -- Need assistance on this line...
FROM "TEST"."TABLE" a
LEFT JOIN (
select s."Type", s."ParentSchema", s."ParentTable", s."ParentColumn", concat(s."ParentSchema",'.','"',s."ParentTable",'"') "FullParentTableName",s."ChildSchema", s."ChildTable", trim(s."ChildColumn",'"') "ChildColumn"
from PUBLIC."SchemaKey" as s
where s."Type" = 'FOREIGN'
and s."ChildTable" = 'SOMETABLENAME'
and "ChildSchema" = 'SOMESCHEMANAME'
) c
on a."column_name" = c."ChildColumn"
Thanks !
In Snowflake you cannot dynamically use the partial results as tables.
You can use a single bound value via identifier to bind a value to table name
But you could write a Snowflake Scripting but it would need to explicitly join the N tables. Thus if you N is fixed, you should just join those.

Use ValueA when JOIN returns a row, otherwise ValueB (like a default)

I have four tables for a form-builder in my databse.
fields (fieldID(PK), typeID, fieldName, ...) - This table is a row by row list of all fields to be in the form
fields_types (typeID(PK), htmlType, ...) - This is a table that links fields to html types (and other settings)
fields_meta (FieldMetaID(PK), FieldID, mName, mValue) - Additional settings for fields, but more specific. A textarea field might have a height attribute, but almost no other field would use that.
fields_tyeps_meta (TypeMetaID(PK), typeID, tmName, tmValue) - Defines what extraneous settings a field can have, and also supplies default values if it's not explicitly set)
So my Query currently looks something like this
SELECT *
FROM Fields F
JOIN Field_Types FT
on FT.FieldID = F.FieldID
LEFT
JOIN Field_Meta FM
on FM.FieldID = F.FieldID
I was wondering if there's a way to join Fields_Types_Meta so that when the row's JOIN to Fields_Meta doesn't return a row (no mValue), it returns tmValue
I realize I can use something like (CASE WHEN mValue = "" THEN tmValue ELSE mValue END) AS UseValue, but I might have fields where I want to allow the value to be set to empty.
Edit: I could probably do something with a subquery and COUNT, using a CASE decision based on that. It might not be the healthiest performance-wise, but this query runs and caches itself til server restart, or until it's told to run again (updates to form design)
It looks like you just want ¢oalesce():
coalesce(FM.mValue, FT.tmValue) as UseValue
When FM.mValue is null, coalesce() returns FT.tmValue instead.
If you have null values in FM that you want to preserve in the result set, then use a case expression instead:
case when FM.FieldID IS NULL THEN FT.tmValue ELSE FM.mValue END as UseValue
This phrases as: when the left join did find a match in FM, use mValue from that row (even if it is null), else use FT.tmValue.

MAX(Date) is giving empty result

I have a table with exchange rate like below
And I am using the maxofdate to pick all these values based on currency code. But the query is giving blank.
Select USDAMOUNT * dbo.EXCHANGERATEAMT
from dbo.Amount_monthly
Left Join dbo.EXCHANGERATE on dbo.Amount_monthly.Currencycode=dbo.EXCHANGERATE.fromcurrencycode
WHERE ValidToDateTime = (Select MAX(ValidToDateTime) from dbo.EXCHANGERATE)
AND dbo.EXCHANGERATE.EXCHANGERATETYPECODE = 'DAY'
Using this statement
CONVERT(DATE,ValidToDateTime) = CONVERT(DATE,GETDATE()-1)
instead of subquery is giving me expected result.
Can someone correct this.
thanks in advance.
If I understand correctly, you need two things. First, the condition for the max() needs to match the condition in the outer query. Second, if you really want a left join, then conditions on the second table need to go in the on clause.
The resulting query looks like:
Select . . .
from dbo.Amount_monthly am Left Join
dbo.EXCHANGERATE er
on am.Currencycode = er.fromcurrencycode and
er.ValidToDateTime = (Select max(er2.ValidToDateTime)
from dbo.EXCHANGERATE er2
where er2.EXCHANGERATETYPECODE = 'DAY'
) and
er.EXCHANGERATETYPECODE = 'DAY';
I would write this using window functions, but that is a separate issue.
Try removing WHERE clause for ValidToDateTime and include it in the JOIN as AND condition
SELECT USDAMOUNT * dbo.EXCHANGERATEAMT
FROM dbo.Amount_monthly
LEFT JOIN dbo.EXCHANGERATE
ON dbo.Amount_monthly.Currencycode = dbo.EXCHANGERATE.fromcurrencycode
AND ValidToDateTime = (SELECT MAX(ValidToDateTime) --remove WHERE clause
FROM dbo.EXCHANGERATE)
AND dbo.EXCHANGERATE.EXCHANGERATETYPECODE = 'DAY';
I cleaned up your query a bit: as the other folks mentioned you needed to close the parentheses around the MAX(Date) sub-query, and if you reference a LEFT JOINed table in the WHERE clause, it behaves like an INNER JOIN, so I changed to in INNER. You also had "dbo" sprinkled in as a field prefix, but that (the namespace) only prefixes a database, not a field. I added the IS NOT NULL check just to avoid SQL giving the "null values were eliminated" SQL warning. I used the aliases "am" for the first table and "er" for the 2nd, which makes it more readable:
SELECT am.USDAMOUNT * er.EXCHANGERATEAMT
FROM dbo.Amount_monthly am
JOIN dbo.EXCHANGERATE er
ON am.Currencycode = er.fromcurrencycode
WHERE er.ValidToDateTime = (SELECT MAX(ValidToDateTime) FROM dbo.EXCHANGERATE WHERE ValidToDateTime IS NOT NULL)
AND er.EXCHANGERATETYPECODE = 'DAY'
If you're paranoid like I am, you might also want to make sure the exchange rate is not zero to avoid a divide-by-zero error.

Joining and filtering one-to-many relationship

I need some help about optimal structuring of SQL query. I have model like this:
I'm trying some kind of join between tables NON_NATURAL_PERSON and NNP_NAME. Because I have many names in table NNP_NAME for one person I can't do one-to-one SELECT * from NON_NATURAL_PERSON inner join NNP_NAME etc. That way I'll get extra rows for every name one person has.
Data in tables:
How to extend this query to get rows marked red on picture shown below? My wannabe query criteria is: Always join name of typeA only if exists. If not, join name of typeB. If neither exists join name of typeC.
SELECT nnp.ID, name.NAME, name.TYPE
FROM NON_NATURAL_PERSON nnp
INNER JOIN NNP_NAME name ON (name.NON_NATURAL_PERSON = nnp.ID)
If type is spelled exactly as it's written (typeA, typeB, typeC) then you can use MIN() function:
SELECT NON_NATURAL_PERSON, MIN(type) AS min_type
FROM NNP_NAME
GROUP BY NON_NATURAL_PERSON
if you also want the username you can use this query:
SELECT
n1.NON_NATURAL_PERSON AS ID,
n1.Name,
n1.Type
FROM
NNP_NAME n1 LEFT JOIN NNP_NAME n2
ON n1.NON_NATURAL_PERSON = n2.NON_NATURAL_PERSON
AND n1.Type > n2.type
WHERE
n2.type IS NULL
Please see this fiddle. If Types are not literally sorted, change this line:
AND n1.Type > n2.type
with this:
AND FIELD(n1.Type, 'TypeA', 'TypeB', 'TypeC') >
FIELD(n2.type, 'TypeA', 'TypeB', 'TypeC')
MySQL FIELD(str, str1, str2, ...) function returns the index (position) of str in the str1, str2, ... list, and 0 if str is not found. You want to get the "first" record, ordered by type, for every NON_NATURAL_PERSON. There are multiple ways to get this info, I chose a self join:
ON n1.NON_NATURAL_PERSON = n2.NON_NATURAL_PERSON
AND n1.Type > n2.type -- or filed function
with the WHERE condition:
WHERE n2.type IS NULL
this will return all rows where the join didn't succeed - the join won't succeed when there is not n2.type that is less than n1.type - it will return the first record.
Edit
If you want a platform independent solution, avoiding the creation of new tables, you could use CASE WHEN, just change
AND n1.Type > n2.Type
with
AND
CASE
WHEN n1.Type='TypeA' THEN 1
WHEN n1.Type='TypeB' THEN 2
WHEN n1.Type='TypeC' THEN 3
END
>
CASE
WHEN n2.Type='TypeA' THEN 1
WHEN n2.Type='TypeB' THEN 2
WHEN n2.Type='TypeC' THEN 3
END
There is a piece of information missing. You say:
Always join name of typeA only if exists. If not, join name of typeB. If neither exists join name of typeC.
But you do not indicate why you prefer typeA over typeB. This information is not included in your data.
In the answer of #fthiella, either lexicographical is assumed, or an arbitrary order is given using FIELD. This is also the reason why two joins with the table nnp_name is necessary.
You can solve this problem by adding a table name_type (id, name, order) and changing the type column to contain the id. This will allow you to add the missing information in a clean way.
With an additional join with this new table, you will be able get the preferred nnp_name for each row.

MYSQL - want to select some rows, sometimes the field won't be there, but i want to use a WHERE on that field when it is there

ok this is hard to explain...
i have the following tables:
table : words
table: word_progress
table: word_set
foreign keys:
words.id = word_set.word_id
word_progress.word_id = words.id
word_progress.user_id = users.id
basically a word is in a word_set. word_progress (it keeps score on a certain word) relates to a user_id and word_id.
the problem is sometimes there is not an entry in word_progress for user_id and word_id. but when it IS there, i wanna be able to use word_progress in the WHERE part of the query. but if it is not there I dont. My work around at the moment is before running the statement, i do an "insert IGNORE IF EXISTS into work_progress (word_id,user_id) values (?,?)" to make sure its there
i have this query
select
words.* ,
word_progress.progress from words
left join word_progress on word_progress.word_id = words.id
left join word_set on word_set.id = words.word_set_id
where word_set.id = ? and word_progress.user_id = ?
but... the problem is sometimes there is no word_progress entry for that word
how can i do this?
you're already left-joining, so when there's no data available, you'll just get null as value for your word_progress-fields. just check for that like this:
...
and
(
word_progress.user_id is null
or
word_progress.user_id = ?
)
another way would be to add the user-restriction directly to the join-criteria like this:
...
left join word_progress
on word_progress.word_id = words.id
and word_progress.user_id = ?
...
and drop that criteria from the where-part.
note that, in both cases, you'll have to handle the null-cases for progress later in your code properly.
yeah, this is ambiguous: " i wanna be able to use word_progress in the WHERE part of the query. but if it is not there I dont. "
in the left joins,
if there's no word_progress to match a word, you'll still get a result row for the word, it's just that all the fields of word_progress will be null.
Same for word_set: no corresponding word_set, then word_set.id and all the rest are null.
so include 'is null' or 'is not null' in your where clause depending on what you want.... think about that case, that's what you left ambiguous. or remember that 'a = b' is false if either a or b is null (even if they're both null), so design your where clause to fit.