In current database design, I have a main table called "Leads" and some other tables starting with product_ ( product_life_insurance, product_medical_insurance , ... )
-Leads Table :
--ID
--Product
...
-Product_Life_insurance Table :
--ID
--LeadID
...
a Lead Row :
ID 5
Product: life_insurance
a product_life_insurance Row:
ID 1
LeadId 5
..
Is there anyway to create a query to select table Name from Leads and add "product_" prefix to it and then join it to product table ?
I mean :
SELECT *
FROM `leads` JOIN `product_life_insurance` ON `leads`.`id` = `product_life_insurance`.`leadID`
WHERE `leads`.`id` = '5';
I want to select table name for join from leads table and add "product_" prefix to it and use it in my query.
Thanks :)
You asked:
Is there any way to create a query to select table Name from Leads and
add the "product_" prefix to it and then join it to product table ?
The answer is no. In pure SQL, you can't make variables of table names and then use them in your queries. You can, of course, do this in your host php code. But you'll need to use one query to fetch the table names, and more queries to fetch the results from that table name.
You can also use Dynamic SQL. That's a MySQL feature allowing you to create the text of SQL queries dynamically in the MySQL server, and then run those queries.
It sounds to me like you're trying to store several classes of entities (life insurance, annuities, vehicle insurance, others) having widely differing attributes.
This presents you with some schema-design options.
Should you use different tables (as you are doing), showing the
entity (lead) class (life insurance) in a master table, and joining
the particular table you need?
Should you try to coerce all the attributes into a single entity,
leaving NULL or blank the attributes that are irrelevant for a
particular class of entity?
Should you use a key/value store for your entities, the way
WordPress's wp_postmeta table does?
Option 3 has a disadvantage if you do a lot of searching on attribute values: it requires your attributes to all be stored with the same data type. That data type is probably varchar(n). That means that it's hard to search on ranges of numeric attribute values. For example '10' is BETWEEN '1' AND '9' considered as text, but that's nonsense numerically. You can beat that problem using implicit typecasting, but that defeats the use of an index. That is,
0+meta_value BETWEEN 0 AND 9
forces the comparison to work numerically on the meta_value column. It works, but not fast. That being said, Option 3 is the most flexible by far; you can add new attributes without changing table definitions.
A combination of Option 2 and Option 3, putting the most commonly searched attribute values into your main lead table, will probably yield the most robust solution.
Option 1 -- your present solution -- is a performance nightmare waiting to attack you when you can least afford it: as your application is scaling up.
NOTE: if you are using the MariaDB fork of MySQL your key_value table can contain a persistent, indexed, virtual column. For example,
meta_key VARCHAR(255),
meta_value VARCHAR(255),
meta_value_int BIGINT(20) AS (0+meta_value) PERSISTENT,
meta_value_float FLOAT AS (CAST(meta_value AS DECIMAL(30,10))) PERSISTENT
You can then index those virtual (defined with AS) columns and search them fast. meta_value columns that don't start with numbers will have the value 0 in the virtual columns.
Related
Using PHP a secure user will enter a Ref (ex. NB093019) a query will be used to determine which PO(s) have that Ref and if they have any quantity. The issue is that we have 86 columns to check if that Ref is in and then once it finds what column it is in how to check the corresponding column that contains that quantity( the table cannot be edited).
I can make this work with 86 if else statements in PHP and then more if else statements inside of each PHP statement. I have no launching point once i do the initial query.
select 'remainder'as prefix, po, *comments,*GuideRef, *Qty
from remainder
where ('NB092419')IN (NWANTcomments,NWANTGuideRef,NWANTpreviouscomments,
NWANTpreviousGuideRef,NWANTprevious2comments,
NWANTprevious2GuideRef, BPrev2GuideRef,
BPrev2comments, BPrevGuideRef, BPrevcomments,
aGuideRef, Mcomments,MGuideRef,acomments,
MAGuideRef,BOGuideRef )
group by po
I have removed some of the in() information so it is not so long also the *comments, *GuideRef, *Qty would be decided by which one of the columns in the IN() statement returns information. Is this even possible
You could perhaps write an SQL that writes an SQL:
select REPLACE(
'SELECT ''{colstub}GuideRef'' as which, {colstub}Qty FROM remainder WHERE {colstub}Ref like ''%somevalue%'' UNION ALL',
'{colstub}',
REPLACE(column_name, 'GuideRef', '')
)
FROM information_schema.columns
WHERE table_name = 'remainder' and column_name LIKE '%Ref'
It works like "pull all the column names out of the info schema where the column name is like %guideref, replace guideref with nothing to get just the fragment of the column name that is varied: NWANTguideref -> NWANT, NWANTpreviousguideref -> NWANTprevious ... then uses this stub to form a query that gives a string depicting the column name, the qty from the quantity column, where the relevant guideref column is LIKE some value"
If you run this it will produce a result set like:
SELECT 'aGuideRef' as which, aQty FROM table WHERE aGuideRef LIKE '%lookingfor%' UNION ALL
SELECT 'bGuideRef' as which, bQty FROM table WHERE bGuideRef LIKE '%lookingfor% ...
So it's basically utputted a load of strings that are SQLs in themselves. It might need a bit of fine tuning, and hopefully all your columns are reliably and rigidly like xQty, xGuideRef, xComments triplets, but it essentially writes most the query for you
If you then copy the result set out of the results grid and paste it back into the query window, remove the last UNION ALL and run it, it will search the columns and tell you where it was found as well as the quantity
It's not too usable for a production system, but you could do the same in php- run the query, get the strings into another sql command, re-run it..
I would suggest you consider changing your table structure though:
prefix, qty, guideref, comments
You shouldn't have 86 columns that are the mostly same thing; you should have one column that is one of 86/3 different values then you can just query the guideref and the type. If this were an address table, I'm saying you **shouldn't* have HomeZipcode, WorkZipcode, UniversityZipcode, MomZipcode, DadZipcode.. and every time you want to store another kind of address you add more columns (BoyfriendZipcode, GirlfriendZipcode, Child1Zipcode...). Instead if you just had an "addresstype" column then you can store any number of different kinds of addresses without recompiling your app and changing your db schema
You can use this technique to re-shape the table - write an SQL that writes a bunch of UNION ALL sqls (without WHERE clauses), one of the columns should be the "recordtype" column (from colstub) and the other columns should just be "qty", "guide", "comments". Once you have your result set with the unions you can make a table to hold these 4 things, and then place INSERT INTO newtable at the head of the block of unions
I need the least expensive way to check if my url slug is formed from the values from two separate columns from two separate tables.
I will use dummy example with stores and locations to make this more human readable.
I have the following url:
www.domain.com/store-location
This could be, for example:
www.domain.com/three-words-store-chicago or
www.domain.com/nicestore-new-york-or-some-neighbourhood-with-more-words or
www.domain.com/oneword-oneword
(you get the idea)
Stores are located in table called stores, and locations in the table called locations.
All the combinations are possible in theory.
So, I would need some clever mysql query combined with php which will check if my slug (what goes after .com/) is the exact combination of store+location. So, to make it more descriptive:
url: www.domain.com/cool-store-los-angeles
Check is there "cool-store" in the table stores.slug_stores and is there "los-angeles" in the table locations.slug_location. The number of words of both is undefined as you can see above, so I don't have any possible delimiter.
IT MUST BE THE LEAST EXPENSIVE WAY because both tables tables have around 1000 lines. PLEASE HELP AND THANK YOU GUYS!
ps. IMPORTANT: I MUSTN'T CHANGE URLS IN ANY WAY
Edit: This is real project, website. Depending on the url i.e. slug I return some view with data. So I need to check for www.domain.com/nicestore-nicecity if Nicestore and Nicecity exist in tables stores and locations, and if not, or if anything else is there like www.domain.com/nicestore-nicecityBLABLA to kill that page with 404. Otherwise, if there is Nicestore and Nicecity to return some page populated with related data. I tried so far to make separate table with formed slugs like "nicestore-nicecity" and to use it for queries "SELECT whatever FROM slugs WHERE whatever = 'nicestore-nicecity' and if there is line return whatever I need to show the page ... Simplified... But, this separate table is hard to maintain. If nicestore moves to uglycity, or if it changes name, or if you add a new store or new city. I hope I was more clear now ;-)
I'm assuming that you don't have any id values on which to JOIN your tables, and that you don't have the ability to create such values. In that case, since your store/location combination could be as short as oneword-oneword, the first and last words of the slug are about as much as you can search on. You can extract the start and end parts of the slug using SUBSTRING_INDEX and use that to narrow the set of matches in each table before you try and compare the whole string. In my example, I'm using an SQL variable to store the slug:
SET #store = 'cool-store-los-angeles'
SELECT *
FROM (SELECT *
FROM stores
WHERE store LIKE CONCAT(SUBSTRING_INDEX(#store, '-', 1), '%')) s
JOIN (SELECT *
FROM locations
WHERE location LIKE CONCAT('%', SUBSTRING_INDEX(#store, '-', -1))) l
WHERE CONCAT(s.store, '-', l.location) = #store
This will return all data associated with cool-store-los-angeles assuming that such a store exists.
Demo on dbfiddle
Here's what I know about your system...
You have a stores table with column slug_stores
You have a locations table with column slug_location
I'm going to assume that each table has an id column of some type. I'm also going to assume they have a many-to-many relationship using a third junction table, something like
CREATE TABLE store_locations (
store_id <type>,
location_id <type>,
PRIMARY KEY (store_id, location_id),
FOREIGN KEY (store_id) REFERENCES stores(id),
FOREIGN KEY (location_id) REFERENCES locations(id)
);
If you don't have this sort of relationship defined, I really don't know how you maintain your store locations.
What I would suggest is creating a VIEW to calculate and represent your URLs. For example...
CREATE VIEW store_location_urls AS
SELECT
sl.store_id,
sl.location_id,
CONCAT_WS('-', s.slug_stores, l.slug_location) AS slug
FROM store_locations sl
INNER JOIN stores s ON sl.store_id = s.id
INNER JOIN locations l ON sl.location_id = l.id;
Now you can use this view to do the following...
Check if a request URL slug is valid
SELECT store_id, location_id FROM store_location_urls WHERE slug = ?
If this returns a record, you can then further query the stores and locations tables for whatever extra data you need to render your page (or even just join them in the original query). Otherwise, use
http_response_code(404);
Get all the URL slugs for a particular store
SELECT slug FROM store_location_urls WHERE store_id = ?
Similarly, you could get all the URL slugs for a particular location
An extra note... due to concatenating strings, any indexes you have on stores.slug_stores and locations.slug_location will be useless with the above VIEW. The alternative is to use a real derived table (like what you currently have) and maintain it with triggers.
I think you can query like following in mysql and if do check in php afterwards. From your description, it doesn't sound like there is any join possible between those tables so, union is required, i think.
select col1,col2 from stores where slug_stores = ?
union
select col1,col2 from locations where slug_location = ?
I'm doing a fairly simple system where users can find computers by searching by option type. I want to search by brand, model, and "options".
Essentially I have 5 tables in this scenario-
brand
model
selection
options_group
options
The selection table is a multi-column lookup table containing:
brand_id
model_id
options_group_id
The options_group table is a lookup table with an ID for "groups of options" and an entry for each option_id.
Basically, the options_group table allows for lots of entries to have the same group of options without storing it more than once.
Right. So. I want to select a specific selection of parts that generates a table:
brand
model
options
where "options" is generated based off the options_group.
My question is this: Do I do this with multiple select statements, where I select just from the selection table first, and then use options_group to do a second select and get all of the options for each row, or do I do a join and get a table with lots of rows?
Before you suggest it, I'm not finding that any of the other answers on SO are answering this exact question.
Or is there some other, better way to do it? I read that joins are orders of magnitude faster than multiple selects, but parsing it at the end could take more time.
use a single statement with select distinct to weed out duplicates. the relational-calculus / relational-algebra that underlies SQL automatically eliminates duplicates as part of the project operator. however, SQL by default does not do so and requires you to use distinct. because underlying relational theory encourages a single statement, and it fits neatly into the operators, i recommend it as a best practice.
with two tables parent (id) and child (id, parent_id, property) do select distinct parent.id from parent join child on parent.id = child.id where child.property in ("X", "Z");
Since you asked for good practice, I'll throw in the fact that this doesn't have to be a db-only solution. It's good practice to cache static/lookup data (sounds like models and/or parts don't change very often) in the app layer or something like memcached, etc, and it will save you the joins and reduce your resultset size.
I have a table in MySQL that I'm accessing from PHP. For example, let's have a table named THINGS:
things.ID - int primary key
things.name - varchar
things.owner_ID - int for joining with another table
My select statement to get what I need might look like:
SELECT * FROM things WHERE owner_ID = 99;
Pretty straightforward. Now, I'd like users to be able to specify a completely arbitrary order for the items returned from this query. The list will be displayed, they can then click an "up" or "down" button next to a row and have it moved up or down the list, or possibly a drag-and-drop operation to move it to anywhere else. I'd like this order to be saved in the database (same or other table). The custom order would be unique for the set of rows for each owner_ID.
I've searched for ways to provide this ordering without luck. I've thought of a few ways to implement this, but help me fill in the final option:
Add an INT column and set it's value to whatever I need to get rows
returned in my order. This presents the problem of scanning
row-by-row to find the insertion point, and possibly needing to
update the preceding/following rows sort column.
Having a "next" and "previous" column, implementing a linked list.
Once I find my place, I'll just have to update max 2 rows to insert
the row. But this requires scanning for the location from row #1.
Some SQL/relational DB trick I'm unaware of...
I'm looking for an answer to #3 because it may be out there, who knows. Plus, I'd like to offload as much as I can on the database.
From what I've read you need a new table containing the ordering of each user, say it's called *user_orderings*.
This table should contain the user ID, the position of the thing and the ID of the thing. The (user_id, thing_id) should be the PK. This way you need to update this table every time but you can get the things for a user in the order he/she wants using ORDER BY on the user_orderings table and joining it with the things table. It should work.
The simplest expression of an ordered list is: 3,1,2,4. We can store this as a string in the parent table; so if our table is photos with the foreign key profile_id, we'd place our photo order in profiles.photo_order. We can then consider this field in our order by clause by utilizing the find_in_set() function. This requires either two queries or a join. I use two queries but the join is more interesting, so here it is:
select photos.photo_id, photos.caption
from photos
join profiles on profiles.profile_id = photos.profile_id
where photos.profile_id = 1
order by find_in_set(photos.photo_id, profiles.photo_order);
Note that you would probably not want to use find_in_set() in a where clause due to performance implications, but in an order by clause, there are few enough results to make this fast.
A friend told me that I should include the table name in the field name of the same table, and I'm wondering why? And should it be like this?
Example:
(Table) Users
(Fields) user_id, username, password, last_login_time
I see that the prefix 'user_' is meaningless since I know it's already for a user. But I'd like to hear from you too.
note: I'm programming in php, mysql.
I agree with you. The only place I am tempted to put the table name or a shortened form of it is on primary and foreign keys or if the "natural" name is a keyword.
Users: id or user_id, username, password, last_login_time
Post: id or post_id, user_id, post_date, content
I generally use 'id' as the primary key field name but in this case I think user_id and post_id are perfectly OK too. Note that the post date was called 'post_date" because 'date' is a keyword.
At least that's my convention. Your mileage may vary.
I see no reason to include the table name, it's superfluous. In the queries you can refer to the fields as <table name>.<field name> anyway (eg. "user.id").
With generic fields like 'id' and 'name', it's good to put the table name in.
The reason is it can be confusing when writing joins across multiple tables.
It's personal preference, really, but that is the reasoning behind it (and I always do it this way).
Whatever method you choose, make sure it is consistent within the project.
Personally I don't add table names for field names in the main table but when using it as a foreign field in another table, I will prefix it with the name of the source table. e.g. The id field on the users table will be called id, but on the comments table it, where comments are linked to the user who posted them, it will be user_id.
This I picked up from CakePHP's naming scheme and I think it's pretty neat.
Prefixing the column name with the table name is a way of guaranteeing unique column names, which makes joining easier.
But it is a tiresome practice, especially if when we have long table names. It's generally easier to just use aliases when appropriate. Besides, it doesn't help when we are self-joining.
As a data modeller I do find it hard to be consistent all the time. With ID columns I theoretically prefer to have just ID but I usually find I have tables with columns called USER_ID, ORDER_ID, etc.
There are scenarios where it can be positively beneficial to use a common column name across multiple tables. For instance, when a logical super-type/sub-type relationship has been rendered as just the child tables it is useful to retain the super-type's column on all the sub-type tables (e.g. ITEM_STATUS) instead of renaming it for each sub-type (ORDER_ITEM_STATUS, INVOICE_ITEM_STATUS, etc). This is particularly true when they are enums with a common set of values.
For example, your database has tables which store information about Sales and Human resource departments, you could name all your tables related to Sales department as shown below:
SL_NewLeads
SL_Territories
SL_TerritoriesManagers
You could name all your tables related to Human resources department as shown below:
HR_Candidates
HR_PremierInstitutes
HR_InterviewSchedules
This kind of naming convention makes sure, all the related tables are grouped together when you list all your tables in alphabetical order. However, if your database deals with only one logical group of tables, you need not use this naming convention.
Note that, sometimes you end up vertically partitioning tables into two or more tables, though these partitions effectively represent the same entity. In this case, append a word that best identifies the partition, to the entity name
Actually, there is a reason for that kind of naming, especially when it comes to fields, you're likely to join on. In MySQL at least, you can use the USING keyword instead of ON, then users u JOIN posts p ON p.user_id = u.id becomes users u JOIN posts p USING(user_id) which is cleaner IMO.
Regarding other types of fields, you may benefit when selecting *, because you wouldn't have to specify the list of the fields you need and stay sure of which field comes from which table. But generally the usage SELECT * is discouraged on performance and mainenance grounds, so I consider prefixing such fields with table name a bad practice, although it may differ from application to application.
Sounds like the conclusion is:
If the field name is unique across tables - prefix with table name. If the field name has the potential to be duplicated in other tables, name it unique.
I found field names such as "img, address, phone, year" since different tables may include different images, addresses, phone numbers, and years.
We should define primary keys with prefix of tablename.
We should use use_id instead if id and post_id instead of just id.
Benefits:-
1) Easily Readable
2) Easily differentiate in join queries. We can minimize the use of alias in query.
user table : user_id(PK)
post table : post_id(PK) user_id(FK) here user table PK and post table FK are same
As per documentation,
3) This way we can get benefit of NATURAL JOIN and JOIN with USING
Natural joins and joins with USING, including outer join variants, are
processed according to the SQL:2003 standard. The goal was to align
the syntax and semantics of MySQL with respect to NATURAL JOIN and
JOIN ... USING according to SQL:2003. However, these changes in join
processing can result in different output columns for some joins.
Also, some queries that appeared to work correctly in older versions
(prior to 5.0.12) must be rewritten to comply with the standard.
These changes have five main aspects:
1) The way that MySQL determines the result columns of NATURAL or USING join operations (and thus the result of the entire FROM clause).
2) Expansion of SELECT * and SELECT tbl_name.* into a list of selected columns.
3) Resolution of column names in NATURAL or USING joins.
4) Transformation of NATURAL or USING joins into JOIN ... ON.
5) Resolution of column names in the ON condition of a JOIN ... ON.
Examples:-
SELECT * FROM user NATURAL LEFT JOIN post;
SELECT * FROM user NATURAL JOIN post;
SELECT * FROM user JOIN post USING (user_id);