View Full Version : Display Output
spotter
11-01-2009, 06:01 AM
I'm looking for a query that can take a table with data in this format:
id name date
1 data1 10-28-09
2 data1 10-28-09
3 data1 10-28-09
4 data2 10-29-09
5 data2 10-29-09
6 data2 10-29-09
7 data3 10-30-09
8 data3 10-30-09
9 data3 10-30-09
and return it in this format
10-28-09 10-29-09 10-30-09
data1 data2 data3
data1 data2 data3
data1 data2 data3
Any suggestions?
Old Pedant
11-01-2009, 06:18 AM
What you are looking for is a "PIVOT" query. I don't think MySQL supports PIVOT.
No...just did a search in the MySQL docs. No mention of PIVOT support.
Some other DBs support it, but if you want this in MySQL, you will probably have to do it via PHP/ASP/JSP coding.
See, the REAL problem you have is what if the data is such that you end up with something like this:
10-28-09 10-29-09 10-30-09
dataA dataX data33
dataB dataY data44
dataC data55
dataD
Old Pedant
11-01-2009, 06:19 AM
I did think of a way to do this in MySQL, but it's really ugly and would probably perform poorly.
But if you MUST have it, let me know.
If you can live with doing the equivalent in PHP/ASP/JSP/whatever, though, you should prefer that.
spotter
11-01-2009, 06:27 AM
I'm all ears, it doesn't matter if it's ugly.
Someone recommending using a CASE statement, but I couldnt wrap my head around it. The statement just returns null values.
SELECT CASE WHEN t.record_date = '10-28-09' THEN t.record_value ELSE NULL END AS '10-28-09',
CASE WHEN t.record_date = '10-29-09' THEN t.record_value ELSE NULL END AS '10-29-09',
CASE WHEN t.record_date = '10-30-09' THEN t.record_value ELSE NULL END AS '10-30-09'
FROM TABLE t
And...The rows will always be the same number, because the dataset is a fixed number of rows.
Are you only going to have those three 'dates'. if not, you will not have a very scalable query using those case statements. Additionally, I would recommend you write a script simply to update your db data for the dates and to use the ISO date format of yyyy-mm-dd
After that, I can't get my head around a solution so it's over to Old Pedant again. ;)
bazz
Old Pedant
11-01-2009, 07:12 PM
And...The rows will always be the same number, because the dataset is a fixed number of rows.
Well, that does simplify it some.
So are those date values *really* in the format you show??? That is NOT a valid date format for MySQL, you know. It *only* supports YYYY-MM-DD.
I'm going to leave them alone and assume that they are text fields and not datetime fields, but you really ought to change that part.
Okay:
CREATE TABLE temp1 ( sequence INT AUTO_NUMBER, name1 VARCHAR(255) );
CREATE TABLE temp2 ( sequence INT AUTO_NUMBER, name2 VARCHAR(255) );
CREATE TABLE temp3 ( sequence INT AUTO_NUMBER, name3 VARCHAR(255) );
INSERT INTO temp1 ( name1 )
SELECT name WHERE `date` = '10-28-09' ORDER BY id;
INSERT INTO temp2 ( name2 )
SELECT name WHERE `date` = '10-29-09' ORDER BY id;
INSERT INTO temp3 ( name3 )
SELECT name WHERE `date` = '10-29-09' ORDER BY id;
SELECT name1 AS `10-28-09`, name2 AS `10-29-09`, name3 AS `10-30-09`
FROM temp1, temp2, temp3
WHERE temp1.sequence = temp2.sequence
AND temp2.sequence = temp3.sequence
ORDER BY temp1.sequence;
I just guessed at VARCHAR(255) for the name field in the temp tables. Adjust as needed.
If you didn't have the same number of rows, it would be tougher because we'd have to do a FULL OUTER JOIN between the 3 tables, instead.
spotter
11-01-2009, 10:42 PM
Thanks for that sql. It seems the task i'm trying to accomplish isn't suited nicely for mysql.
I'm gonna attempt to output the same thing with text files and php, which many have already said is a better approach.
If anyone has suggestions for this problem in php, i'll be making a similar post under the php questions.
No No, don't make the mistake I did.
I went with text files in 2004 because it was easier and I understood it better. I also had a mental blockage about MySQL - I had it in my head that you would have to be a propfessional programmer to use it and I don't regard myself as one of those.
Yet in 2007/8 (can't recall), I switched to dip my toe into MySQL and since then, I haven't looked back. Much more powerful system and much easier to maintain and manage.
I would recomend you try to restructure your data into a normalised fashion. I am confident that once done, you will have a much easier time and within about a year, you'll wonder why you ever thought of text files. ;)
If you want to post or pm me, your data 'headings' I'll put together a normalised solution from which it should be simpler to get a hold on it. If nothing else, I will have doubled the value of FishMongers advice to me in 2004/5 to use MySQL.
baz
Coyote6
11-02-2009, 07:50 PM
$q = SELECT name, date FROM table;
// Query the database to put the values into an array....
// $results[] = array(name, date)
// Start a new array to reorder.
$records = array();
// Loop through each of these and place into an array sorted by date.
foreach($results as $r) {
$records[$r['date']][] = $r['name'];
}
// Loop through this array now since it is an array of just the dates that contains an array of its values. You can add you're own html tags to this.
foreach ($records as $key=>$value) {
echo $key;
foreach ($value as $v) {
echo $v;
}
}
I'm gonna attempt to output the same thing with text files and php, which many have already said is a better approach.
Not sure which other forums you have asked on but, be careful not to dismiss the valuable advice that people (not including me), give here.
It is because the advice has been forthcoming and correct, that I have learned so much and why I have stick with CF. I have seen other forums where they are full of learners and their advice can sometimes send you up a dead end.
I am quite capable of sending myself up a dead end and CF people have shown me the better way :)
bazz
Coyote6
11-06-2009, 05:44 AM
Okay so here is the code to make that work... the code I posted originally works for the unordered lists so I had to get a little creative for the table method.
CREATE TABLE `table` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`name` VARCHAR( 50 ) NOT NULL ,
`date` DATE NOT NULL DEFAULT '0000-00-00',
UNIQUE (`name`)
);
INSERT INTO `table`
(`name`, `date`)
VALUES
('data1', '2009-10-28'),
('data2', '2009-10-28'),
('data3', '2009-10-28'),
('data4', '2009-10-29'),
('data5', '2009-10-29'),
('data6', '2009-10-29'),
('data7', '2009-10-30'),
('data8', '2009-10-30'),
('data9', '2009-10-30');
<?php
/*********** Database Connection *******************/
// Database access information.
define ('USER', '');
define ('PASSWORD', '');
define ('HOST', '');
define ('DATABASE', '');
// If connection to MySQL works then check to see if the database is selected.
if ($dbc = @mysql_connect (HOST, USER, PASSWORD)) {
// If database could not be selected then write an error message.
if (!mysql_select_db (DATABASE)) {
echo 'Could not select the database';
exit();
}
}
// If the connection to MySQL did not work, print a message to the user, include the footer and kill the script.
else {
echo 'Could not connect to the database.';
exit();
}
/***************************** End of Database Connection File *****************/
// Query.
$q = "SELECT `name`, `date` FROM `table`";
// Query the database to put the values into an array.
$r = @mysql_query ($q);
$results = array();
if (mysql_num_rows ($r) > 0) {
while ($row = mysql_fetch_assoc ($r)) {
$results[] = $row;
}
}
// Start a new arrays.
$records = array();
$table = array();
$columns = array();
// Loop through each of these and place into an array sorted by date if using unorder lists
foreach($results as $r) {
$records[$r['date']][] = $r['name'];
}
// Loop through and make the date the array key if using tables.
foreach ($records as $key=>$value) {
$i=0;
foreach ($value as $k=>$v) {
if (!in_array ($r['date'], $columns)) {
$table[$i][$key] = $v;
}
$i++;
}
}
// Loop through the results to get the the headers into a variable and the data into a variable.
$hdrs = '';
$data = '';
foreach($table as $key=>$value) {
$data .= " <tr>\n";
foreach ($value as $k=>$v) {
if ($key == 0) {
$hdrs .= " <th>" . htmlspecialchars($k) ."</th>\n";
}
$data .= " <td>" . htmlspecialchars ($v) . "</td>\n";
}
$data .= " </tr>\n";
}
$i++;
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Pivot</title>
<style type="text/css">
ul {width: 150px; float: left; border: 1px solid black; padding: 0px;}
li {border: 1px solid black; padding: 1px; margin: 2px; list-style-type: none;}
.label, th {font-weight: bold;}
</style>
</head>
<body>
<p>This is the output in Table format:</p>
<table cellpadding="1" cellspacing="2" border="1">
<thead>
<tr>
<?php
echo $hdrs;
?>
</tr>
</thead>
<tbody>
<?php
echo $data;
?>
</tbody>
</table>
<p>This is the output in List format:</p>
<?php
// Loop through this array now since it is an array of just the dates that contains an array of its values. You can add you're own html tags to this.
foreach ($records as $key=>$value) {
echo " <ul>\n" .
" <li class=\"label\">$key</li>\n";
foreach ($value as $v) {
echo " <li>$v</li>\n";
}
echo " </ul>\n";
}
?>
</body>
</html>
:thumbsup:
spotter
11-06-2009, 08:24 AM
Here's a tricky one. How would you put a alphabetical value sort() on each of the columns?
foreach ($records as $key=>$value) {
$i=0;
foreach ($value as $k=>$v) {
if (!in_array ($r['date'], $columns)) {
$table[$i][$key] = $v;
}
$i++;
}
}
Coyote6
11-06-2009, 05:30 PM
Opps!!! delete the in_array columns statement and the columns variable. It was from earlier tests. That section should read.
// Start a new arrays.
$records = array();
$table = array();
// Loop through each of these and place into an array sorted by date if using unorder lists
foreach($results as $r) {
$records[$r['date']][] = $r['name'];
}
// Loop through and make the date the array key if using tables.
foreach ($records as $key=>$value) {
$i=0;
foreach ($value as $k=>$v) {
$table[$i][$key] = $v;
$i++;
}
}
Then to sort you need to use the sort() function. So here is the completed section.
// Start a new arrays.
$records = array();
$table = array();
// Loop through each of these and place into an array sorted by date if using unorder lists
foreach($results as $r) {
$records[$r['date']][] = $r['name'];
}
// Echo
foreach ($records as $key=>$value) {
sort ($records[$key]);
}
// Loop through and make the date the array key if using tables.
foreach ($records as $key=>$value) {
$i=0;
foreach ($value as $k=>$v) {
$table[$i][$key] = $v;
$i++;
}
}
And to sort the table after it loads on the page use the jQuery table sorter link I sent you in the email. Just read the docs and it will explain how to use it.
http://tablesorter.com/docs/
Enjoy!!!
Coyote6
11-06-2009, 05:32 PM
Also to sort the dates this should work.
sort ($records);
IN your names table, you have assigned a unqiue index to 'names'. don;t forget that ID's are needed only to give a record somethihng unique so with a unique range of names, the id ain't needed.
bazz
Coyote6
11-06-2009, 08:46 PM
Yes it is not necessary but if you allow duplicate names, then your tables are no longer normalized. The only time I would allow this is for instances such as a User table where people have the same name, but then if you added a social_security_field or email_field it could be unique.
If the names field carries duplicate data, it should be stored into another table and accessed with a foreign key.
table1
id | name
1 | data1
2 | data2
3 | data3
table2
id | table1_id | date
1 | 1 | 2009-10-28
2 | 2 | 2009-10-28
3 | 3 | 2009-10-28
4 | 1 | 2009-10-29
5 | 2 | 2009-10-29
And even on an instance such as table2 it should have a unique index on table1_id & date together. You could also accomplish this by dropping the primary keys but then it makes your tables harder to reference as foreign keys.
For instance if you were to drop the primary key and use name as the primary then it becomes harder to reference as a foreign key.
table1
name
data1
data2
data3
table2
table1_id | date
data1 | 2009-10-28
data2 | 2009-10-28
data3 | 2009-10-28
data1 | 2009-10-29
data2 | 2009-10-29
-- Now say you want to reference table 2 as a foreign key.
table2_id | some_data
data1, 2009-10-28 | blah
-- Instead of something simple like
table2_id | some_data
1 | blah
Yes it is not necessary but if you allow duplicate names, then your tables are no longer normalized.
I disagree. Even if the name John were to be used twice, it would be relating to two different people and so it is not breaching normalisation rules. I have plenty of people in my db, who have the same data for specific things, eg some address data. Being separate records, repetitive data doesn't break normalisation.
If the names field carries duplicate data, it should be stored into another table and accessed with a foreign key.
If this were to be done, and John were to be removed; all Johns would be deleted when the foreign key constraint is applied, as it ought to be. There is a big difference between two pieces of data 'reading' the same and two pieces of data being the same. That said, it is likely inconceivable that a name (or first name, last name) should be used as primary keys because so many poeple have the same first and last names.
And even on an instance such as table2 it should have a unique index on table1_id & date together. You could also accomplish this by dropping the primary keys but then it makes your tables harder to reference as foreign keys.
[quote]
Not so. Using textual data as primary keys often facilitiates easier db maintenance because you can see straight off, what the data is. Otherwise, you would need to flip between tables to see that the primary key meant
This last code which shows two pieces of data in the one column is erroneous because storing two separate pieces of data in one field breaks 1NF. I have written out the create table statements which I hope will help to explain one use of a composite primary key. 'composite primary keys' is the issue here.
create table names
( name varchar (99) not null
, security_number varchar(99) not null
primary key (name, security_number)
) engine.....
create table number_two
( name varchar(99) not null
, security_number varchar(99) not null
, some_data varchar(255) not null
constraint number_two_names_fk
foreign key (names,security_number)
references names(name, security_number)
) engine.....
column names used are not the best for this example because security_numbers are themselves unique. However, to demonstarte how composite keys work, it's ok.
I have left out the constraint instructions such as on delete cascade and on update cascade to try to keep it simple. cascade can be any one of four options where, by leaving it out, it defaults to restrict. Remember too, that the data types of PK and FK must match eg varchar(99) must be the same for both tables (or whatever the correct data type for your app would be).
bazz
Coyote6
11-06-2009, 11:54 PM
I think you misunderstood what I was saying. I said that I would allow for duplicate data in tables such as User fields. I really do not know what kind of data he is putting in. Maybe it is unique data that needs to go into the field, maybe it isn't. What I meant was that if you can I would recommend putting them into unique fields so you only have to update the database 1 time if something changes, not everywhere you used a name as a foreign key.
Ex. With a name as a key.
name | other_data
data1 | 1
data2 | 2
data3 | 3
-And the data was referenced in two other tables as a key.
id | name | more_info
1 | data1 | has 1 records
2 | data1 | has 2 records
3 | data2 | some more info
etc.
id | name | last_piece
1 | data1 | some_data
etc.
Now let's say data1's name changed to data100 you would have to go through and look for ever instance data1 was used as a foreign key in every table and change it to data100 to get it to work. Is there a function I do not know about that would fix this?
Now what if it was used in the url?
Ex. A company table all with unique names
Companies
name
Fish & Fry Grill
Sunny Side Cleaners
Companies
id | name
1 | Fish & Fry Grill
2 | Sunny Side Cleaners
If you had the need to call the company name from a url you would end up with spaces or invalid characters in the url name.
get_company.php?name=Fish & Fry Gril
get_company.php?name=Sunny Side Cleaners
Yes it may work as in the second instance if the names are simple but is not good url syntax. The first one would break because it uses and & sign and it would think it was looking for another variable, where as
get_company.php?id=1
get_company.php?id=2
Will work.
Yes I completely agree with you there are times in which you end up with duplicate data in a field I was just saying you should stay away from it if the data is indeed the same.
Now let's say data1's name changed to data100 you would have to go through and look for ever instance data1 was used as a foreign key in every table and change it to data100 to get it to work. Is there a function I do not know about that would fix this?
Constraints.
If there is a table with a primary key of 'name' and other tables where this is a foreign key, then using a constraint with 'on update cascade' means that not only would you change the pk but MySQL would cascade that change to every fk that was related to it.
get_company.php?name=Fish & Fry Gril
get_company.php?name=Sunny Side Cleaners
I agree :) that isn't good syntax for a url but business names aren't guaranteed unique anyway.
My reading is that the data in a db table to be declared as a PK, need only be unique. If none of the data is guaranteed to be unique ~ either as a single field or a composite of several ~ then a auto_increment PK should be used.
Apologies if my previous post was a bit terse. Bad day at the ossiff.
bazz
Coyote6
11-07-2009, 06:37 AM
Constraints.
If there is a table with a primary key of 'name' and other tables where this is a foreign key, then using a constraint with 'on update cascade' means that not only would you change the pk but MySQL would cascade that change to every fk that was related to it.
Learn something new every day :cool:
Thanks for the info... No problem sounds like your day went about as well as my week... been busting my butt trying to meet an impossible deadline. Well it was possible when I began and I did have the project finished, but a week before it was due we decided to change it.... yippeee :rolleyes: So ya now its questionable.
And now that the weekend is here it's time to work on my own projects.
Learn something new every day :cool:
:cool:
vBulletin® v3.8.2, Copyright ©2000-2012, Jelsoft Enterprises Ltd.