I have a (nice) mysql table built like this :
Fields Datas
id (pk) 1 2 3 4 5 6
master_id 1000 1000 1000 2000 2000 2000 ...
master_name home home home shop shop shop ...
type_data value common client value common client ...
param_a foo_a 1 0 bar_a 0 1 ...
param_b foo_b 1 0 bar_b 1 0 ...
param_c foo_c 0 1 bar_c 0 1 ...
... ... ... ... ... ... ... ...
All these datas are embed in a single table. Each datas are dispatched on 3 "columns" set (1 for the values, 1 for identifying if these are common values and one for identifying client values). It's not the best I got but many other scripts depends on this structure.
I'd need sthg like this:
SELECT parameters name (eg param_a, param_b..) and their values (eg foo_a, foo_b..)
WHEN master_id=? AND type_data=(common or client) (eg for values=1 on the 2nd column)
.
in order to get the parameters hash for a particular master_id like
param_a => foo_a
param_b => foo_b
param_c => foo_c
...
I could not succeed in self joining on the same table till now but I guess it should be feasible. (I'd like to avoid to do several queries)
Thx in advance
I think you are talking about pivoting the data? If so, see here: http://en.wikibooks.org/wiki/MySQL/Pivot_table and here: http://dev.mysql.com/tech-resources/articles/wizard/print_version.html
Related
I have 2 tables, Tags and Certs, in a one-to-many relationship - each tag will have anywhere between 0 and 10+ certs associated with it.
Tags Certs
[Tag] [Other fields] [Tag] [Date] [Length]
A-22 ... A-22 03/05/17 265
P-63 ... A-22 15/02/14 331
... A-22 09/12/12 656
P-63 01/02/12 1024
...
Is there a way to return the most recent, second most recent, third most etc. length for each Tag as different fields?
My ideal end product would look something like:
[Tag] [Most recent length] [Second most recent] [third most] ...
A-22 265 331 656
P-63 1024
...
This issue is especially grating since I recently ported it from MySQL, which has the LIMIT clause which solved this easily. I'm aware there is no direct equivalent (from MS Access Limit), but I'm wondering if there is some other way to achieve what I need.
Thanks to HansUp and Foxfire, I was able to find a solution. Posting here so it can maybe help others having a similar problem.
First step was to construct a ranking query from the Certs table:
SELECT t1.[Tag], t1.[Date], t1.[Length], COUNT(t1.[Date]) AS Rank
FROM [Certs] AS t1, [Certs] AS t2
WHERE (t1.[Date] <= t2.[Date]) AND t1.[Tag] = t2.[Tag]
GROUP BY t1.[Tag], t1.[Date], t1.[Length]
ORDER BY t1.[Tag];
Which gave the result:
[Tag] [Date] [Length] [Rank]
A-22 03/05/17 265 1
A-22 15/02/14 331 2
A-22 09/12/12 656 3
P-63 01/02/12 1024 1
...
From there a simple crosstab query constructed from the wizard with [Tag] as row headers, [Rank] as column headers, and MAX([Length]) as values, produced the desired table in the question.
I have a MySQL database with the two tables that I need modified.
The first table holds notes
id type note
1 1 24 months warranty
2 1 12 months warranty
3 2 Garage in Denver
4 3 Pre sales maintenance done
....
And then a vehicle table that holds many vehicle tables and a field that hold notes with their text instead of a pointer
id licence_plate ... sales_notes ...
1 XH34DN ... <warranty>24 months warranty</warranty><garage>Garage in Denver</garage><maintenance>Pre sales maintenance done</maintenance> ...
2 K4B3C6 ... <warranty>12 months warranty</warranty><garage>Garage in Sacramento</garage><maintenance>Pre sales maintenance not done</maintenance> ...
As you can imagine this is higly inneficient and I want to modify to pointers that hold the id of the note.
id licence_plate ... warranty_note garage_note maintenace_note ...
1 XH34DN ... 1 3 4 ...
2 K4B3C6 ... 2 7 12 ...
I can do it manual updates but I would like to build one that makes it automatically by type.
So for notes.type=1 if the notes.note text is found in vehicle.sales_notes it updates the vehicle.warranty_note.
Any idea how to build something like that?
I have something like this in mind, but id doesn't work. No results are updated
UPDATE tx_vehicle v, tx_note n
SET v.garage_note = n.uid
WHERE v.sales_notes LIKE ('%'+n.note+'%')
MySQL has special XML parsing functions.
insert into your_new_notes_table (id, licence_plate, warranty_note, garage_note, maintenace_note)
select
sn.id,
sn.license_plate,
(select nt.id from notes as nt where nt.type = 1 and nt.note = ExtractValue(sn.sales_note, "/warranty")) as warranty_note,
(select nt.id from notes as nt where nt.type = 1 and nt.note = ExtractValue(sn.sales_note, "/garage")) as garage_note,
(select nt.id from notes as nt where nt.type = 1 and nt.note = ExtractValue(sn.sales_note, "/maintenance")) as maintenance_note
from note_types as nt
Although, it will fail if exact note from sales is not found in note_types table. You can adjust note text comparison, replacing it with like operator, regex checking or another functions; you can replace abbreviations with full words or invent your own MySQL function. Moreover, you can invent your own MySQL function that would encompass internal selects and return null if note is not found in note_types table.
If you want to update existing table, you need to perform update query with join to same select as I provided.
What SQLite statement do I need to get the column name WHERE there is a value?
COLUMN NAME: ALPHA BRAVO CHARLIE DELTA ECHO
ROW VALUE: 0 1 0 1 1
All I want in my return is: Bravo, Delta, Echo.
Your request is not entirely clear, but you appear to be asking for a SELECT statement that will return not data but rather columns names, and not a predictable number of values but rather a number values that depend on the data in the table.
For instance,
A B C D E
0 1 0 1 1
would return (B,D,E) whereas
A B C D E
1 0 1 0 0
would return (A, C).
If that's what you're asking, this is not something that SQL does. SQL retrieves data from the table and an SQL result set always has the same number of columns per row.
To accomplish your goal, you would have to retrieve all columns that might have a value in the table and then, in your program code, check for the value in each column and accrue a list of column names that had values.
Also, consider what happens when there is more than one row to examine and the distribution of values differ. In other words, what's the expected result if the data looks like this:
A B C D E
- - - - -
0 1 0 1 1
1 0 1 0 0
[Also, note that all the columns in your example have values, some 0, some 1. What you really want is a list of column names where the column contains a value of 1.]
Finally, consider that your inability to easily get the results you need from your data might indicate a flaw in the data model you're using. For instance, if you were to structure your data like this:
TagName TagValue
------- --------
Alpha 0
Bravo 1
Charlie 0
Delta 1
Echo 1
you could then obtain your results with SELECT TagName FROM Tags WHERE TagValue = 1.
Furthermore, if 0 and 1 are really the only two possible values (indicating boolean "presence" or "absence" of the tag) then you could remove the TagValue column and the rows for Alpha and Charlie entirely (you'd INSERT a row into the table to add tag and DELETE a row to remove it).
A design along these lines seems to model your data more accurately and allows you to entire new tags to the system without having to issue an ALTER TABLE command.
http://sqlfiddle.com/#!9/1407e/1
SELECT CONCAT(IF(ALPHA,'ALPHA,',''),
IF(BRAVO,'BRAVO,',''),
IF(CHARLIE,'CHARLIE,',''),
IF(DELTA,'DELTA,',''),
IF(ECHO,'ECHO',''))
FROM table1
I want to query severity/facility from syslog, and translate then from number to meaningful keywords like this:
select case severity
when 0 then 'emerg'
when 1 then 'Alert'
when 2 then 'Crit'
when 3 then 'Error'
when 4 then 'Warn'
when 5 then 'Notice'
when 6 then 'Info'
when 7 then 'Debug'
end,
case facility
when 0 then 'kern'
when 1 then 'user'
...
when 23 then 'local7'
end
from logs.sys_log;
While the range of severity is from 0 to 7, and the range of facility is from 0 to 23.
I will get a very long query string.
Is there any smarter method to create key->value mapping in MySQL, to shorten the query string ?
Create new tables severity_mapping and facility_mapping with two columns:
number
value
And store the data 0-emerg etc. to first table and 0-kern to the second. Later, use JOIN clauses in your query.
I have the following 2 tables
table_article:
id subject tags
---------------------
1 subject-1 2,4,5
2 subject-2 3,5
3 subject-3 1,2
4 subject-4 2,3,4
5 subject-5 3
table_tags:
id tag_name
---------------------
1 php
2 jQuery
3 css
4 mysql
5 java
and I'm trying to get results like
id => 1, subject => subject-1, tag_names => jQuery,mysql,java
id => 2, subject => subject-2, tag_names => css,java
id => 3, subject => subject-3, tag_names => php,jQuery
Here is my current attempt, which returns ONLY the first tag (e.g. 2 instead of 2,4,5 for row 1)
1 SELECT
2 table_article.id,
3 table_article.subject,
4 GROUP_CONCAT(table_tags.tag_name) AS tag_names
5 FROM
6 table_article
7 LEFT JOIN
8 table_tags
9 ON
10 (table_tags.tag_id IN (table_article.tags))
11 GROUP BY
12 table_article.id
13 LIMIT
14 3
and the results are
id => 1, subject => subject-1, tag_names => jquery
id => 2, subject => subject-2, tag_names => css
id => 3, subject => subject-3, tag_names => php
The problem occurs on line 10 -> IN (table_article.tags).
I just can't figure out how could I solve this problem, can anyone help please?
You can't use a string that happens to contain commas as a list of discrete values.
In other words this:
ON table_tags.tag_id IN (2,4,5)
Is not the same as this:
ON table_tags.tag_id IN ('2,4,5')
The numeric value of a string like '2,4,5' is the initial numeric portion, and the remainder after the first non-numeric character is ignored. So the string '2,4,5' has a numeric value of 2. It won't be an error, but it won't get you what you intended, which is a match against any of the values in the comma-separated list.
MySQL has a built-in function FIND_IN_SET() which does understand strings that contain comma-separated values. The function returns the position of the matching value, or 0 if no match was found.
ON FIND_IN_SET(table_tags.tag_id, '2,4,5') > 0
But this cannot use an index and it forces you to run a table-scan which is going to kill your performance. To be clear, I don't recommend using this function in a join condition.
The answer is: Don't store tags in a comma-separated list. See my answer for Is storing a comma separated list in a database column really that bad?
Store one tag per row in a separate table, as #Martin Lyne suggests. That way you can look for the right tag with = and you can even index the column for much better performance.
I've not seen an IN in a ON before (not saying it's not valid) but I would do ON table_tags.tag_id = table_article.tags)
So you end up with multiple rows
subject-1, query
subject-1, css
subject 2, query
then the GROUP BY would compress the table and the GROUP_CONCAT gets all the missing tags.
Well, I would use IN in this situation, it won't work, replace it with FIND_IN_SET(table_tags.tag_id, table_article.tags) > 0 and you'll be fine. Though you really should normalize this.
As other said, this is not a good design.
Instead, you could change your table design this way:
table_article
id
subject
article_tag
article_id
tag_id
table_tags
id
tag_name
Life would be much easier this way :-)