sql IN reverse usage - mysql

I am trying to show products belongs to that location but I failed.
db_products.locationId stores id's string like 1,2,5,9
:locationId stores location id like 5 I send this variable as parameter to sql query.
SELECT db_products.*,
db_assets.path
FROM db_products INNER JOIN
db_assets ON db_assets.guid = db_products.guid
WHERE db_products.pcId = 1 AND
:locationId IN (db_products.locationId)
ORDER BY db_products.id

Fix your data structure! Do not store integers as strings in a list. Why not?
Integers should be stored as numbers, not strings.
SQL has poor string handling.
Foreign key relationships should be properly declared.
The resulting queries cannot make use of indexes.
SQL has a great data structure for storing lists. It is called "table".
So, use a junction table. You can look it up.
Sometimes, we are stuck with other people's really bad design decisions. In this case, MySQL has find_in_set():
SELECT p.*, a.path
FROM db_products p INNER JOIN
db_assets a
ON a.guid = p.guid
WHERE p.pcId = 1 AND
FIND_IN_SET(:locationId, p.locationId) > 0
ORDER BY p.id;

Related

Join to tables and String Compare (large data set)

I am very new to SQL and don't really know much about what i'm doing. I'm trying to figure out how to get a list of leads and owners whose corresponding campaign record types are stated as "inter"
So far I have tried joining the two tables and running a string compare I found on a different stack overflow page. Separately they work fine but together everything breaks... I only get the error "You have an error in your SQL syntax; check the manual"
select a.LeadId, b.OwnerId from
(select * from CampaignMember as a
join
select * from Campaign as b
on b.id = a.CampaignId)
where b.RecordTypeId like "inter%"
Schema:
Campaign CampaignMember
------------- ----------------
Id CampaignId
OwnerId LeadId
RecordTypeId ContactId
The string compare is also very slow. I am looking at a table of 600M values. Is there a faster alternative?
Is there also a way to get more specific errors in MySQL?
If you format your code properly, it will be very easy to see why it's not working.
select a.LeadId, b.OwnerId
from (
select *
from CampaignMember as a
join select *
from Campaign as b on b.id = a.CampaignId
)
where b.RecordTypeId like "inter%"
It's not a valid JOIN format. Also the last part, SQL use single quote ' instead of double quote "
Probably what you want is something like this
SELECT a.LeadId, b.OwnwerId
FROM CampaignMember a
JOIN Campaign b ON b.id = a.CampaignId
WHERE b.RecordTypeId LIKE 'inter%'
Try this:
select CampaignMember.LeadId, Campaign.OwnerId from
Campaign
inner join
CampaignMember
on CampaignMember.CampaignId= Campaign.id
where Campaign.RecordTypeId like "inter%"
MySql is generally pretty poor and handling sub-selects, so you should avoid them when possible. Also, your sub-select isn't filtering any rows, so it has to evaluate every row before applying the LIKE filter. This is sometimes "intelligently" handled by the query engine, but you should try to minimize reliance on the engine to optimize the query.
Additionally, you really should only return the columns that you care about; SELECT * is ok for confirming things, but slows queries down.
Therefore, the query posted by Eric (above) is actually the best choice.

Renaming a Key on a multi-table MySQL SELECT

I'm trying to get the content of 3 different tables.
table A = Is containing our users list, table B = Is returning contracts related to users,table C = Is returning formula details related to the contracts.
In order to make it the right way, I'm using the following multi table request:
SELECT * FROM rscm_students A, rscm_files B, rscm_formulas C
WHERE B.dossier_status = 0
AND A.student_agency = :agency
AND B.file_student_id = A.id
AND B.file_formula_id = C.id
AND C.formula_place = 0
GROUP BY A.student_uniqid
ORDER BY B.file_date_create";
This is where the whole damn thing become a little complicated. It is returning the correct datas, but as the primary key of every table here is called "id". I can't do some foreach in php. If I got 3 contracts on 1 user, it impossible for me to regroup every contract in the same user array.
I'm still not an expert in SQL, that's why I'm using Phinx to control my database. This is also why my primary keys are named "id".
If you have a good idea, please let me know!
Alright, I will make an answer out of it.
First off, don't use
select *
The above select is fine for quick and dirty development prior to production. But it makes a mess out of things such as your joins with common column names coming out of multiple tables (like id and others).
Use modern explicit join syntax. Don't use the older join style. So use join and on.
Lastly with table aliases, create unique output column names for the id columns or other clashes such as
A.id as aid, B.id as bid

MySQL Where IN with an Array

I've searched a bit but haven't found exactly what I'm looking for, so far. Basically I have a MySQL database with a couple tables (keywords, company and link tables). I want to be able to supply an array of keywords and find the associated companies. When I run the second query without the WHERE IN clause, it works. When I supply an array, it doesn't.
select keywords.idkeywords into #keyId
from keywords where
keywords.keyword IN ('Entertainment', 'Public');
select distinct company.company_name
from keywords, keyword_to_company, company
where keyword_to_company.keywordId = #keyId
and keyword_to_company.compId = company.idcompany;
Your query just doesn't make sense. First, you are trying to put multiple values in #keyid, but you can't do that. And, MySQL doesn't have the concept of table variables. You could use a temporary table.
Then the second query is worse. Does this query work for you?
select distinct c.company_name
from keywords k natural join
keyword_to_company k2c natural join
company c
where k.keyword IN ('Entertainment', 'Public') and
k2c.compId = company.idcompany;
I'm only using natural join because you don't specify the join keys. In general, you should always specify the columns being joined, using either on or using.
Thanks...
Your query didn't work..
This query does work however. Althought #keyId returns multiple rows, the query succeeds and results in a listing of associated companies. i agree that it shouldn't work, but it does.
select keywords.idkeywords into #keyId from keywords where
keywords.keyword = 'Entertainment'
and
keywords.keyword = 'Public';
select distinct company.company_name from keywords, keyword_to_company, company
where
keyword_to_company.keywordId = #keyId
and
keyword_to_company.compId = company.idcompany;

How to retrieve "dynamic" attributes stored in multiple rows as normal records?

I have a system built on a relational MySQL database that allows people to store details of "leads". In addition, people can create their own columns under which to store data and then when adding new accounts can add data under them. The table structure looks like this:
LEADS -
id,
email,
user_id
ATTRIBUTES -
id,
attr_name,
user_id
ATTR_VALUES -
lead_id,
attr_id,
value,
user_id
Obviously in these tables "user_id" refers to a "Users" table that just contains people that can log into the system.
I am writing a function to output lead details and currently am just pulling through the basic lead details as a query, and then pulling through every attribute value associated with that lead (joining on the attributes table to get the name) and then joining the arrays in PHP. This is a little messy, and I was wondering if there was a way to do this in one SQL query. I have read a little about something called a "pivot table", but am struggling to understand how it works.
Any help would be greatly appreciated. Thanks!
You could do the pivoting in a single query like the following:
select l.id lead_id,
l.email,
group_concat(distinct case when a.attr_name = 'Home Phone' then v.value end) HomePhone,
...
from leads l
left join attr_values v on l.id = v.lead_id
left join attributes a on v.attr_id = a.id
group by l.id
You will need to include a separate group_concat-derived field for each attribute you want to display.
I would have a look at this link. That explain the fundamental of a pivot:
"pivot table" or a "crosstab report" SQL Characteristic Functions: Do
it without "if", "case", or "GROUP_CONCAT". Yes, there is use for
this..."if" statements sometimes cause problems when used in
combination. The simple secret, and it's also why they work in almost
all databases, is the following functions: sign (x) returns -1,0, +1
for values x < 0, x = 0, x > 0 respectively abs( sign( x) ) returns 0
if x = 0 else, 1 if x > 0 or x < 0 1-abs( sign( x) ) complement of the
above, since this returns 1 only if x = 0
It a also explain a more simple way of pivoting exams. Maybe this can shed some light over it?
What you probably want from mysql is to make an sql value (attr_name in your case) a column. This principle is called pivot table (sometimes also cross tables or crosstab queries) and is not supported by mysql. Not because mysql is insufficient, but because the pivot operation is not a database operation - the result is not a normal database table and is not designed for further database operations. The only purpose of pivot operation a presentation - that's why it belongs to presentation layer, not database.
Thus, every solution of trying to get a pivot table from mysql will always be hacky. What I recommend is to get the data from database in normal format, by simply doing something like:
select *
from attr_values join attributes using on attr_id = attributes.id
join leads on leads.id = lead_id
and then transform the database output in the presentation language (PHP, JSP, Python or whatever you use).
I'll be careful to assume that pivot will achieve your simplification goal. Pivot will only work if you attr_name are consistent. Since you tied a userid to it, I assume it wouldn't. In addition, you will have multiple values for one attr_name. I'm afraid pivot table wouldn't produce the result you are looking for.
I would suggest that you keep your transactional and reporting tables separate. Have an ETL routine that will clean (ie. make the attr_name and attr_value) consistent through translation. This will make your reports more meaningful.
In summary, for immediate output to end-user, PHP is the best you can do. For reporting, transform the EAV to a row/column first before attempting to report on it.

Delphi 2009, MyDAC and relational database

I have quite a problem concerning the use of relational database concepts in Delphi 2009 with MyDAC.
I have a database structure that looks somehow like the following:
Item
id
name
Storage
id
name
StorageItem
id
item_id
storage_id
place
Now when I have an active dataset from "Item" how can I display all associated Storages in for example a DBGrid?
By the way: Would it be better to not use "id" in every table but to alter it and use something like for example "id_item" or "id_storage"?
Thank you in advance :)
With StorageItem you created a
many-to-many relationship. If you
need just one-to-many (many storages
are related to one item, but you
don't need the vice versa), then you
may just add another field to the
Storage table (item_id) that would
be a foreign key for Items table.
Then you create an index on
item_id in Storage table, and
connect the two tables in
master-detail relationship.
If you do need many-to-many then you
may add a query component with SQL
(select * from StorageItem where
item_id := :current_storage_id), and
current_storage_id is your query's
parameter.
Select a.ID, b.Name, a.Place
from StorageItem a
inner join Storage b
on (a.id = b.id)
the above query will return all the items in StorageItem table with it's name, now if you want to filter it to return only items for a specific item add where clause to be like
Select a.ID, b.Name, a.Place
from StorageItem a
inner join Storage b
on (a.id = b.id)
where a.item_id = 1 -- place the item id here
you can use where with parameters such as:
MyQuery.Sql.Text := ' Select a.ID, b.Name, a.Place from StorageItem a
+ ' inner join Storage b on (a.id = b.id) '
+ ' where a.item_id = :ItemNo ';
MyQuery.ParamByName('ItemNo').asInteger := 1;
MyQuery.Open;
and assign the query above to dbGrid
also you can use MasterSource property to make the relations without using the "where" part
I'm not familiar with MyDAC personally, but most dataset components have some way to establish master-detail relationships. Check if there's a MasterSource property on your dataset, or some similar way to link a detail dataset to a master dataset. If not, you could use a TDatasetField to establish a link, and filter the nested dataset to only display the right records.
As for ID column names, it's a good idea to give a descriptive name to each field, so you can tell by looking at the code that you've got your links right. If you call your id column "id", that could be any id column, and that could get confusing if you start passing around references to datasets. But if it's called item_id every time, (not item_id sometimes and id_item sometimes) then you always know exactly what you're looking at. It makes it easier to know that your code is right, too. A filter that says "master.item_id = detail.item_id" is easier to read that "master.id = detail.item_id". That could be wrong and fail silently if master is assigned to the wrong dataset, for example.