How to select vertical data in MySQL table - mysql

If I have a table like this:
ID color size weight
1 red 2 3
2 green 4 5
So to run a mysql query to find the ID number that is color:red and size:2 and weight:3 I can do this:
select ID from table where color=red AND size=2 AND weight=3
As properties are growing in addition to color, weight, size, mileage, speed, etc... and I want to keep the table scaling it would make sense to organize it this way
ID ID2 property value
1 1 color red
2 1 size 2
3 1 weight 3
4 2 color green
5 2 size 4
6 2 weight 5
How do I run a select query here to find the ID number that is color:red and size:2 and weight:3

For key value structure one approach is to self join as many times as the no. of properties you have like in your case 3 properties (color,weight,size) so you need 2 self joins and for each,filter results according to properties like in below query i have given unique aliases so for the alias t i have filtered rows with t.property = 'color' and for value t.value = 'red' and same as for other aliases to find the desired ids which fits for the provided criteria,So if your criteria increases with another property you need another self join and same filtering as above
select t.id2
from test t
join test t1 on(t.id = t1.ID2)
join test t2 on(t.id = t2.ID2)
where t.property = 'color'
and t1.property = 'size'
and t2.property = 'weight'
and t.value = 'red'
and t1.value = '2'
and t2.value = '3'
DEMO

If there's a lot of properties (only a few of which may be of interest), you may be better off with the Star schema that was specifically invented for this case.
Less than 10 properties you described are hardly a justification for such complication though. You'll be just fine with the initial table in accordance with the KISS principle.

Related

Mysql diversified results

I got table in db in following way:
id Name Type
1. Aero. Product
2. Ddd. Product
3. Sass. Image
4. Rrrrr. Image
This is just to understand and actual table in much bigger scale.
So the question is how to get diversfied results so the product type wont be like product,product, image, image
If i will do select * from table where 1 order by ‘id’
Results having grouped “type”
I want have results like
1 blabla product
3 blabla image
2 blabla product
4 blabla image
So the records with same type will be spreaded over results and
As much as possible wont stay together
As already suggested its better if you do this type of sorting in your application code, In Mysql you can do this by using user defined variables.
First break your data according to your types, get data for products and assign a row number and do the same with second data set for type image and then merge these sets using union operation and then sort them by row number
select *
from(
select d1.*, #row1:= #row1 + 1 as row
from demo d1,
(select #row1:=0) t1
where `Type` = 'Product'
union all
select d2.*, #row2:= #row2 + 1 as row
from demo d2,
(select #row2:=0) t2
where `Type` = 'Image'
) t
order by t.row, t.Type desc
demo

SQL unwanted results in NOT query

This looks like it should be really easy question, but I've been looking for an answer for the past two days and can't find it. Please help!
I have two tables along the lines of
texts.text_id, texts.other_stuff...
pairs.pair_id, pairs.textA, pairs.textB
The second table defines pairs of entries from the first table.
What I need is the reverse of an ordinary LEFT JOIN query like:
SELECT texts.text_id
FROM texts
LEFT JOIN text_pairs
ON texts.text_id = text_pairs.textA
WHERE text_pairs.textB = 123
ORDER BY texts.text_id
How do I get exclusively the texts that are not paired with A given textB? I've tried
WHERE text_pairs.textB != 123 OR WHERE text_pairs.textB IS NULL
However, this returns all the pairs where textB is not 123. So, in a situation like
textA TextB
1 3
1 4
2 4
if I ask for textB != 3, the query returns 1 and 2. I need something that will just give me 1.
The comparison on the second table goes in the ON clause. Then you add a condition to see if there is no match:
SELECT t.text_id
FROM texts t LEFT JOIN
text_pairs tp
ON t.text_id = tp.textA AND tp.textB = 123
WHERE tp.textB IS NULL
ORDER BY t.text_id ;
This logic is often expressed using NOT EXISTS or NOT IN:
select t.*
from texts t
where not exists (select 1
from text_pairs tp
where t.text_id = tp.textA AND tp.textB = 123
);

SQL: How to check "if this record exists then that record must also exist" for given ID set

my database table (DWInfo) looks like this:
InstanceID | AttributeID
1 | 1
1 | 2
1 | 3
2 | 1
2 | 4
3 | 1
3 | 2
There are several instances and every instance has multiple attributes.
What I want to achieve is this: for a given set/rule of id's I want to get all InstanceID's which violate the condition, for example let the given ID's be 1 and 2, which means if there is an instance with AttributeID=1, Attribute=2 should also exist for it. In this case the result would be instance two, because this instance violates the condition.
I tried it with JOINS but this only seemed effective for 2 attributes and not more.
Select * from DWInfo dw1 INNER JOIN DWInfo dw2 ON dw1.InstanceID = dw2.InstanceID where dw1.AttributeID != dw2.AttributeID and dw1.AttributeID = 1 AND dw2.AttributeID != 2
Is it possible to solve this problem with a SQL query?
Assuming that each InstanceId can have only one of each different AttributeId, i.e. a unique composite index (InstanceId, AttributeId):
SELECT InstanceID
FROM DWInfo
WHERE AttributeID IN (1,2)
GROUP BY InstanceID
HAVING SUM(AttributeId = 1) = 1
AND COUNT(*) < 2 /* Or SUM(AttributeId = 2) = 0 */
SQLFiddle DEMO
Note that if having AttributeId of 2 means that the instance requires an AttributeId of 1 also.. slightly different logic, this is neater:
SELECT InstanceID
FROM DWInfo
WHERE AttributeID IN (1,2)
GROUP BY InstanceID
HAVING COUNT(*) < 2
Where there exists Attribute 1 find the ones that don't have Attribute 2.
select InstanceID
from DWInfo
group by InstanceID
having
count(case when AttributeID = 1 then 1 end) > 0
and count(case when AttributeID = 2 then 1 end) = 0
This answer is basically the same as Arth's. You might find it beneficial to filter the Attributes in the where clause but it's not strictly necessary. I prefer the standard syntax using case expressions even though the shorthand would be handy if it were portable. I also prefer count over sum in these scenarios.
It's not clear whether you can have duplicates (probably not) and whether Attribute 2 can appear alone. You might have to tweak the numbers a bit but you should be able to follow the pattern.
I think this does what you want:
select instanceid
from dwinfo
where attributeid in (1, 2)
group by instanceid
having count(*) = 2;
This guarantees that you have two matching rows for each instance. If you can have duplicates, then use:
having count(distinct attributeid) = 2
EDIT:
For the conditional version (if 1 --> 2):
having max(attributeid = 2) > 0
That is, if it has 1 or 2, then it has to have 2, and everything is ok.

MySQL take duplicate data and combine unique data

With my MySQL database, I want to take data from my temporary table and insert it into my main table, while removing any duplicate data but also taking into consideration the data I already have. This seems to require an update and/or an insert depending on what exists in "data_table" so I really have no idea how to write it or if it is even possible. If this isn't possible, I'd like to know how to accomplish this while not considering what is already in "data_table" which I would think is possible. Thank you for your help!
Existing data_table before running query:
data_table
+-----id-----+-----age-----+-----gender-----+-----color-----+
=============+==============+=================+================+
1 5 m pink,red,purple
data_table_temp
+-----id-----+-----age-----+-----gender-----+-----color-----+
=============+==============+=================+================+
1 5 m red
2 5 m blue
3 5 m red
4 5 m orange
5 6 m red
6 6 m green
7 6 m blue
After query:
data_table
+-----id-----+-----age-----+-----gender-----+-----color-----+
=============+==============+=================+================+
1 5 m pink,red,purple,blue,orange
2 6 m red,green,blue
Here is an approach to this problem which turned out to be harder than I expected.
The idea is to concat the colors that don't match and put them together. There is a bit of a problem assigning ids. Getting the "2" for the second row is a problem, so this just assigned the id sequentially:
select #id := #id + 1 as id,
coalesce(dt.age, dtt.age) as age,
coalesce(dt.gender, dtt.gender) as age,
concat_ws(dt.color,
group_concat(case when find_in_set(dtt.color, dt.color) > 0
then dtt.color
end)
)
from data_table_temp dtt left outer join
data_table dt join
on dt.age = dtt.age and
dt.gender = dtt.gender cross join
(select #id := 0) var
group by coalesce(dt.age, dtt.age), coalesce(dt.gender, dtt.gender);
MySQL doesn't have any string functions to (easily) split a delimited string (like data_table.color).
However, if you have all of the data in data_table_temp's format (one color per row), you can generate the desired results like this:
SELECT DISTINCT age, GROUP_CONCAT(DISTINCT color)
FROM table WHERE [condition]
GROUP BY age;
Optionally adding in gender, as necessary.
Apologies for the half-answer

How can I sort my table with elements and subelements

I have a MySQL table similar to this:
ID elementName subElementOfID
1 1
2 2
3 4
4 5
5 9
6 3
7 6
8 7
9 71 8
10 8
11 10
12 72 8
13 73 8
The elements are not necessarily numeric but can be varchars. The column "subElementOfID" means that this element ("71" for example) is a subelement of the element with ID X. In this example the elements with the IDs 9, 12 and 13 are subelements of the element with ID 8.
Target:
Ordering this table in numeric or alphanumeric order (that depends but is not really important) with the Elements marked as subelements sorted directly under their parent elements. The SELECT output ordered in numeric order in this case should look like:
ID elementName subElementOfID
1 1
2 2
6 3
3 4
4 5
7 6
8 7
9 71 8
12 72 8
13 73 8
10 8
5 9
11 10
The output is processed later in PHP. I would prefer though not to use a PHP solution but a SQL-Select which gives me the table in the right order.
Any help or advice is very much appreciated.
Since this is my first Stackoverflow question I hope I have collected all important facts and am prepared to provide further information :-)
Ralph
I found a solution involving a SELF JOIN and a UNION. This may be overly complicated and I would very much like an easier SELECT.
SELECT ID, element FROM
((SELECT t1.ID, t1.elementName as element, t2.elementName AS element2, t1.subElementOfID
FROM test t1
LEFT JOIN test t2 ON t2.ID = t1.subElementOfID
WHERE t1.subElementOfID IS NOT NULL)
UNION
(SELECT ID, elementName AS element, elementName AS element2, subElementOfID FROM test
WHERE subElementOfID IS NULL)
ORDER BY element2, subElementOfID, element) tu
User xpda (thanks for your input!) suggested to order by a string concatenation. This is actually a great idea I think, if the elementName column actually contains a name and not a number, because I'm not totally content with alphanumeric ordering of numbers.
In real live I have integers in this column but may convince my boss to use verbal names not numbers. Trying to write a SQL expression out of my understanding of xpda's answer:
SELECT t1.ID, t1.elementName, t1.subElementOfID AS parentName
FROM test2 t1
LEFT JOIN test2 t2 ON t1.subElementOfID=t2.ID
ORDER BY IF(ISNULL(t2.elementName), t1.elementName, CONCAT(CAST(t2.elementName AS CHAR), CAST(t1.elementName AS CHAR)));
So all child elements are ordered under their parents but "10" is naturally between 1 and 2...
Any further suggestions?
You can sort on (order by) a concatenation of the two columns, the second column + first column. You may need to convert to string first.
Ok, I'm going to answer my own question :-) The comment from Barmar helped as well as the answer from xpda. They didn't provide SQL but lead me in the right direction. Thank your very much!
I have two working solutions, each with it's merrits:
I make a simple self join and order by a concatenated string which I enrich with some leading zeros for single digit numbers. In other cases - when sorting varchar values - these zeros are not necessary:
SELECT t1.ID, t1.elementName, t1.subElementOfID AS parentName
FROM table t1
LEFT JOIN table t2 ON t1.subElementOfID=t2.ID
ORDER BY IF(ISNULL(t2.elementName), LPAD(t1.elementName, 2, '0'), CONCAT(CAST(LPAD(t2.elementName, 2, '0') AS CHAR), CAST(t1.elementName AS CHAR)));
The SQL from above with self join and UNION works fine even if it's a bit longish:
SELECT ID, element FROM
((SELECT t1.ID, t1.elementName as element, t2.elementName AS element2, t1.subElementOfID
FROM table t1
LEFT JOIN table t2 ON t2.ID = t1.subElementOfID
WHERE t1.subElementOfID IS NOT NULL)
UNION
(SELECT ID, elementName AS element, elementName AS element2, subElementOfID FROM table
WHERE subElementOfID IS NULL)
ORDER BY element2, subElementOfID, element) tu