I'm trying to do a customizable and extendable profile system for my CMS. From the user perspective it is straight forward, but for the admin I want all data, including the profile data, to be searchable. Profile fields may be added by "plugins", which may also add new fields to search on. I don't know if what I'm trying to do with MySQL to make this work is possible or if I'm going at it completely the wrong way.
So I have the users stored in one table (users), with columns for id, email, password and access_level.
I then have another table with profile information (profiles), stored as user_id, parameter and value. The parameter could eventually be put into a separate table again (so it isn't repeating itself), but for now I'll leave it like this.
The parameter and value are basically the profile data. For example, parameter may be "age" and the value may be "22".
What I want to try and do, is select the users table, with the profile information joined so the parameter is mapped to an additional column. So it ends up like so, straight from MySQL:
id email password access_level age
1 a#a.com ***** 1 22
2 b#b.com ***** 2 25
3 c#c.com ***** 2 25
I've been looking at pivot tables all afternoon, but from all I can see the "column name" is pre-defined. In this case I want the "column name" to come from the row itself.
If it isn't possible to do it with a single query, what other methods are there? I'm using PHP if the best method is to do it via that.
Any suggestions would be much appreciated. :)
Well, if you need to know the column names in advance, you can query the information_schema database:
SELECT COLUMN_NAME
FROM information_schema.COLUMNS
WHERE TABLE_NAME='your table'
However, that gets the raw column names. If you're aliasing in your query, you'll have to fetch them indirectly:
SELECT somefield AS alias1, otherfield AS alias2
FROM ...
and then
$stmt = mysql_query($query);
$first = true;
while($row = mysql_fetch_assoc($stmt)) {
if ($first) {
$column_names = array_keys($row);
... display column names here
$first = false;
}
... output row here
}
Related
I'm trying to add a function to a script that orders a list of users and then allows that same order SQL to be used to create an export file. I'm writing it for a piece of software that hosts basic user data in one table, and the question value I'm trying to get in another. Here's the details:
Table1 is base_user and from it I need the values of columns id, username, and email. However, I want to add the option to order/export by users with all that same data, but by their sex.
Table2 is base_question_data and from it I want to get the question 'sex' value.
Users can pick between Male or Female (Values: 1 or 2), and that info is stored in a column named intValue. I've already tried using INNER JOINS and such selecting multiple info from both tables, but where I'm getting confused is how to say "Get id, username, email, and sex for every user that is X sex" where "X sex" is the gender the user set to order/export by. I'm having a hard time figuring out how to get the specifics for the sex value for each user, but also use it to only show all those users of that value. All ideas are appreciated.
EDIT:
Forgot to mention that in the 'base_question_data' table, column 'userId' is equal to column 'id' in 'base_user' table.
This is Table1 or base_user
This is Table2 or base_question_data
To clarify what I'm trying to do:
In the 'base_user' table, I want to select ID, Username, and Email. I have this working normally, as it's a simple query. I want to, however, let users order by each user's gender. So
1)They can order the preview list by Male (questionName = sex intValue = 1) or Female (questionName = sex intValue = 2). This way, it will show all user's ID, Username, and Email who are gender 1 or 2. Using this same query, I'm trying to let them export that data as well, so they can export only users of gender 1 or 2.
My problem is the process of combining all of this data. I need to combine base_user's "id" to base_question_data's "userId" and I need to also get the value of each user by targeting base_question_data's questionName='sex' and get the intValue based on if they're ordering by Male (value=1) or Female (value=2).
I've done LEFT JOINS when combining one value to another for two tables, and while this is still two tables, I've never done it where I need to combine two different keys from two tables while also ordering them all by two different column values in one of those tables.
I agree with #rowmoin but if you use LEFT JOIN it will give null value if no entry in your base_question_data table and if you use INNER JOIN it will ignore those records from both tables which does not match so you can not get users from base_user if you do not have related entries in base_question_data table.
The query was easier than I anticipated. I will explain it in detail here.
To achieve something like this, (in which I needed one table's two values to decide what I'm getting in my other table results), I simply changed the value of 'intValue' in my query, but you can also do this by assigning a php value to let it be dynamically changed if you want. Since I'm doing a change between 1 or 2, I just have to different queries with the respective variable.
Here's my query:
SELECT * FROM ow_base_user
INNER JOIN ow_base_question_data ON ow_base_user.id = ow_base_question_data.userId
WHERE questionName='sex' AND intValue='$value';
I finally realized this morning I needed to go backwards, rather, and select my conditionals from base_question, rather than from the base_user table. Using this query, (where $value=1 for male or $value=2 for Female) I have successfully gotten to return a list of user's who match the set $value with their IDs, Username, and Email.
I am trying to replace a column in the result of the select query as denoted in
This reference but unlike the example I have many columns in the table thus I can not specify the name of every column in the select query.
I tried some ways to attain the same but none seems effective.
select
*, (REPLACE(REPLACE(role_id,1,"admin"),2,"moderator") AS role_id
from user;
or
Select *
from user
where role_id = (select REPLACE(role_id,1,"admin") as role_id from user;
Here we assume only two possible values for the role_id however at certain instanced it might have to get data from another table ie a different table that holds different ids and values corresponding to them.
So is there a way to attain the following conditions in a single query:-
to replace values of some fields returned from select query (assuming many columns writing the names of all the columns individually is not feasible)
to get the replacement values from different tables for different columns in single table.
I need to implement the above conditions in one query but the changes shouldn't be in the database only the result of select query needs to be optimized.
Already referred to the following too but could not help.
Link 1
Link 2
Link 3
I am using phpmyadmin as engine and php as the implementation language.
If i have understood your question correctly, it's easier to use CASE/WHEN
SELECT *,
CASE WHEN role_id = 1 THEN "admin" WHEN role_id = 2 THEN "moderator" END AS role_id
FROM user;
But easier still maybe to have an array in PHP,
$roles = array("1" => "admin", "2" => "moderator", .... );
and look it up in the array. that will keep your query short and sweet. The advantage of this approach is that you don't need to change your query every time you add a new role. If you get a large number of roles (say dozens) you might actually want a separate table for that.
I'm currently trying to write a simple ORM with PHP and mysql. I want the orm class to be able to work with joined tables.
So here's my problem, the following code shows how I map the data the query yields into an array.
public function execute_query($db_connection)
{
$query = '';
foreach($this->sql_query as $query_part)
$query .= $query_part;
$result = $db_connection->query($query);
while($row = $result->fetch_assoc())
{
array_push($this->m_Data, $row);
}
}
db_connection is a mysqli object.
sql_query contains all the different query parts (e.g. sql_query['join'] etc.).
m_Data is the array that contains the data read from the db.
My specific problem now is when I'm using a join statement in my query this function will just override fields with the same name in my m_Data array. Also if I dont save the name's of the table the specific field data is coming from, I later can't update the tables with the same join statement.
tl,dr. I need to be able to not only save the table data like this: m_Data{ 'field_name' => 'value' } but I also need to save the table name the field is selected from. I could then save the data like this m_Data{ 'table_name.field_name' => 'value' } which enables me later to generate a query to update the joined tables successfully.
I cant seem to find any information on how to get the origin table name for each field I pull out of the result.
If it isnt possible with mysqli I'd much appreciate it if you point me in the right direction.
extra short problem statement:
I need to get a result set and read each row seperatly. For each row I need the following information for every field selected: field_name, table_name, value.
There must be a simple answer to this but I seem to be searching for the wrong keywords to find a solution.
I hope I've written this understandable enough.
Seems to me that you should store table column values in an object, so if you have a related table, the column values would be stored in a separate object - and so would not interfere with the values in your primary table.
In general you might work with the ORM this way:
// Make joined query
$rows = ...
foreach($rows as $row)
{
// $row just refers to the primary table
echo $row->id;
// You get a many:1 related table this way
echo $row->getRelatedRow()->value;
// You get a 1:many rows this way
$rows = $row->getOtherRelatedRows();
}
Depending on how you set up your query options, getting related data may or may not initiate further SELECTs to get the required data.
mysqli_result::fetch_fields has useful things:
http://www.php.net/manual/en/mysqli-result.fetch-fields.php
table
orgtable
field type
etc
Actually, I have no idea how does it looks like the question's title, is it exactly related to the issue or not, but I'm gonna try explain my problem:
I have a table named as AdvertClick (that stores the stats of each "Advert" by "AdvertID") and Advert table besides (as you guess the main table that stores my ad lists). So I need to store each advert click data (i.e. "Country","Browser","Language" etc.) in AdvertClick table. For example, the code is below getting top country data from AdvertClick table.
SELECT Count(_ac.ID) AS Click, _ac.Country
FROM `Advert` _a
LEFT JOIN `AdvertClick` _ac ON _ac.AdvertID = _a.ID
WHERE _a.UserID = $UserID
GROUP BY _ac.Country
ORDER BY Click DESC
LIMIT 1
But, I've replicated AdvertClick table as AdvertClick0, AdvertClick1 ... AdvertClick9, because it's (the unique stats table) was getting so heavy and slower. And now, I have 10 tables and all off them are same formed (meaning table colums). I'm just inserting every click data like this;
$TableName = "AdvertClick". $AdvertID % 10;
$SQL = "INSERT INTO ${TableName} ... VALUES (...)";
So, now I want to do same thing above but failing.
I've tried CREATE VIEW in very different ways like;
CREATE ALGORITHM = TEMPTABLE VIEW _tmp_ (colums...) SELECT x,y,z -> fail
CREATE VIEW _tmp_ (colums...) SELECT x,y,z JOIN AdvertClick0 _ac0 -> fail
etc...
Is anyone help me about this (really annoying) issue?
Thanks in advance...
hm... I think view would be appropriate for this kind of situation you should also have a good structured table to get the proper approach on this another approach I would do is to add another column that will identify what type of data are on that row for example:
table: advert
columns: name, country, type
the type will take any value you want or you may create another table and reference it from there as a foreign key to make it more flexible in that case you'll be having only 2 tables. To make if work as a view you can do it this way:
CREATE VIEW 'AdvertClick1' AS SELECT name, country FROM advert WHERE type = 1;
CREATE VIEW 'AdvertClick2' AS SELECT name, country FROM advert WHERE type = 2;
CREATE VIEW 'AdvertClick3' AS SELECT name, country FROM advert WHERE type = 3;
and so on and so forth. :)
i will propose something else entirely.
i think you should keep only one AdvertClick table - DO NOT create many of them and therefore create these other headaches. To get you answers will be even more difficult and even more slow.
Instead, if you are sure you have optimized your queries, you may choose to denormalize selectively to gather your common statistics.
for example:
create an ON INSERT trigger on the single AdvertClick table that increments the count value in a new AdvertClickByCountry table for the current Country. Also, create an ON DELETE trigger that decrements that count for the country.
Then your query above would be trivial - something like:
SELECT click_count, country
from AdvertClickByCountry
ORDER BY Click_count DESC
LIMIT 1
This is hopefully a quick php question...
I have stored a number of id's in a field on a user table like '1,2,3'. I now want to query another table against those numbers. I think my brain has gone to mush this morning because can't seem to get it right...
Could 'IN' be used in the query?
Update -
Realised I didn't explain my self well enough...
I have two tables, one for users (id, username, password, products) The 'products' field has comma separated id's in which related to another table which holds product information.
I am basically trying to filter out what a user can see, via a query to the database with that users privileges.
You should fix your database schema; then, querying will be obvious and efficient.
Every column in your database should contain atomic values. If you've stored multiple values in a single column, it means you should have created a table with a one-to-many relation.
CREATE TABLE user_whatever (user_id int, whatever_id int)
...with one row per item related to the user. To query another table against those numbers, you simply JOIN this new table in the query.
You may be better off using a join, hard to tell from your question what sort of data you're working with.
Join's would be far more efficient with large amounts of data.
without knowing your database structure it's impossible to help further
SELECT column_names
FROM table_one
INNER JOIN table_two
ON table_one.column_name=table_two.column_name
is the basic syntax.
You can use IN in SQL without any problem, but you will need to convert the PHP array to a compatible list for example using the implode(separator,array) function...
[Update] Seeing the question update: The technique you chose for saving the "products" is not very good and WILL cause you problems in the future. the standard solution for storing these values is creating an additional table (userid,productid) and the primary key is both fields in it you create multiple rows for each user/product permission.
To actually use the specific solution you created you will need to run an SQL getting the values from the table, and then using a SECOND SQL use the WHERE IN $ids to actually get the data (as far as I know you can not use the IN on a string result like this (no such thing as EVAL in SQL)
Like everybody said, you should use atomic values. But since everybody already explained that, I'm only going to clarify in detail what you should do.
Use a table for users, which contains the id, username and password, but without product
id - username - password
1 - mike - XXXX
2 - joe - XXXX
Then you have your products table
id - name - price
1 - Aluminum bat - 19.99
2 - Scattergun - 39.99
3 - BONK (with isotopes) - 14.99
Now, you introdouce a third table, which represents what users have bought.
user - product
1 - 2
1 - 3
2 - 1
Now, if you want to select the products that mike has bought, you just select all rows from the third table where the user id is equal to 1. In conjunction with foreign keys and indexes, the queries should also have a better performance. For clarification, this is how my example would look like in your current implementation.
id - username - password - products
1 - mike - XXXX - 2,3
2 - joe - XXXX - 1
$sql = "SELECT * FROM table
WHERE id IN (";
$i=1;
$count = count($array);
foreach($array as $a){
if($i==$count){
$sql .= $a['id'];
}else{
$sql .= $a['id'].','
}
$i++;
}
$sql .= ")";
Please try this..
Get the field with the ids ('1,2,3') from your first table into some variable -> $ids
$sql = sprintf(SELECT * FROM second_table WHERE id IN (%s),$ids);
Hope this helps.
ps.: I don't recommend storing id this way.