Unique value through two columns - mysql

Is there any constraint in MySQL in order to avoid from entering the same value in the same column or in another column?
For example, in the following table:
--------------------
| TABLE |
---------------------
| NUMBER1 | NUMBER2 |
---------------------
| 3 | 4 |
| 7 | 9 |
---------------------
I need to prevent the number 3 from being entered in any of the columns NUMBER1 or NUMBER2, since the value already exists in column NUMBER1; and that the number 9 cannot be ingested in either of the columns NUMBER1 or NUMBER2, since the value already exists in column NUMBER2.
In other words, I need that each value in each of the two columns be unique, through those two columns.
Thank you in advance.

Here is an indirect solution. You may refactor the design of your table such that it uses only a single column for both current columns. Then, introduce some sort of group columns to keep track of the relationships between the numbers from a given record. That is:
grp | pos | NUMBER
1 | 1 | 3
1 | 2 | 4
2 | 1 | 7
2 | 2 | 9
Now you only need a unique constraint on the single NUMBER column. Note that if you want to view your data as it appears now, a simple pivot query can handle that:
SELECT
grp,
MAX(CASE WHEN pos = 1 THEN NUMBER END) AS NUMBER1,
MAX(CASE WHEN pos = 2 THEN NUMBER END) AS NUMBER2
FROM newTable
GROUP BY
grp;

Related

MySQL table order by one column when other column has a particular value

I have two mysql tables record_items,property_values with the following structure.
table : property_values (column REC is foreign key to record_items)
id(PK)|REC(FK)| property | value|
1 | 1 | name | A |
2 | 1 | age | 10 |
3 | 2 | name | B |
4 | 3 | name | C |
5 | 3 | age | 9 |
table: record_items
id(PK) |col1|col2 |col3|
1 | v11| v12 | v13|
2 | v21| v22 | v23|
3 | v31| v32 | v33|
4 | v41| v42 | v43|
5 | v51| v52 | v53|
record_items table contains only basic information about the record, where as property_values table keeps record_item as a foreign key and each property and its value is saved in a separate row.
Now I want to get the record_items sorted based on a particular property, say by age.
My HQL query will be like
Select distinct rec from PropertyValues where property="age" order by value;
But this query will be skipping record 2 since it don't have an entry for property age.
I expect the result to have the records which contains age property in sort order appended by those which don't have age property at all. How can I query that?
Here is a raw MySQL query which should do the trick:
SELECT t1.*
FROM record_items t1
LEFT JOIN property_values t2
ON t1.id = t2.REC AND
t2.property = 'age'
ORDER BY CASE WHEN t2.value IS NULL THEN 1 ELSE 0 END, t2.Value
I notice that your Value column in property_values is mixing numeric and text data. This won't work well for sorting purposes.
Demo here

INSERT data from one table INTO another with the copies (as many as `quantity` field in first table says)

I have an MySQL table creatures:
id | name | base_hp | quantity
--------------------------------
1 | goblin | 5 | 2
2 | elf | 10 | 1
And I want to create creature_instances based on it:
id | name | actual_hp
------------------------
1 | goblin | 5
2 | goblin | 5
3 | elf | 10
The ids of creatures_instances are not important and not relevant to creatures.ids.
How can I make it with just the MySQL in the most optimal (in terms of execution time) way? The single query would be best, but procedure is ok too. I use InnoDB.
I know that with a help of e.g. php I could:
select each row separately,
make for($i=0; $i<line->quantity; $i++) loop in which I insert one row to creatures_instances for each iteration.
The most efficient way is to do everything in SQL. It helps if you have a numbers table. Without one, you can generate the numbers in a subquery. The following works up to 4 copies:
insert into creatures_instances(id, name, actual_hp)
select id, name, base_hp
from creatures c join
(select 1 as n union all select 2 union all select 3 union all select 4
) n
on n.n <= c.quantity;

SELECT from Union x 3 using filter of another table

Background
I have a web application which must remove entries from other tables, filtered through a selection of 'tielists' from table 1 -> item_table 1, table 2, table 3.... now basically my result set is going to be filthy big unless I use a filter statement from another table, using a user_id... so can someone please help me structure my statement as needed? TY!
Tables
cars_belonging_to_user
-----------------------------
ID | user_id | make | model
----------------------------
1 | 1 | Toyota | Camry
2 | 1 |Infinity| Q55
3 | 1 | DMC | DeLorean
4 | 2 | Acura | RSX
Okay, Now the three 'tielists'
name:tielist_one
----------------------------
id | id_of_car | id_x | id_y|
1 | 1 | 12 | 22 |
2 | 2 | 23 | 32 |
-----------------------------
name:tielist_two
-------------------------------
id | id_of_car | id_x | id_z|
1 | 3 | 32 | 22 |
-----------------------------
name: tielist_three
id | id_of_car | id_x | id_a|
1 | 4 | 45 | 2 |
------------------------------
Result Set and Code
echo name_of_tielist_table
// I can structure if statements to echo result sets based upon the name
// Future Methodology: if car_id is in tielist_one, delete id_x from x_table, delete id_y from y_table...
// My output should be a double select base:
--SELECT * tielists from WHERE car_id is 1... output name of tielist... then
--SELECT * from specific_tielist where car_id is 1.....delete x_table, delete y_table...
Considering the list will be massive, and the tielist equally long, I must filter the results where car_id(id) = $variable && user_id = $id....
Side Notes
Only one car id will appear once in any single tielist..
This select statement MUST be filtered with user_id = $variable... (and remember, i'm looking for which car id too)
I MUST HAVE THE NAME of the tielist it comes from able to be echo'd into a variable...
I will only be looking for one single id_of_car at any given time, because this select will be contained in a foreach loop.
I was thinking a union all items would do the trick to select the row, but how can I get the name of the tielist the row is in, and how can the filter be used from the user_id row
If you want performance, I would suggest left outer join instead of union all. This will allow the query to make efficient use of indexes for your purpose.
Based on what you say, a car is in exactly one of the lists. This is important for this method to work. Here is the SQL:
select cu.*,
coalesce(tl1.id_x, tl2.id_x, tl3.id_x) as id_x,
tl1.y, tl2.idz, tl3.id_a,
(case when tl1.id is not null then 'One'
when tl2.id is not null then 'Two'
when tl3.id is not null then 'Three'
end) as TieList
from Cars_Belonging_To_User cu left ouer join
TieList_One tl1
on cu.id_of_car = tl1.id_of_car left outer join
TieList_Two tl2
on cu.id_of_car = tl2.id_of_car left outer join
TieList_Three tl3
on cu.id_of_car = tl3.id_of_car;
You can then add a where clause to filter as you need.
If you have an index on id_of_car for each tielist table, then the performance should be quite good. If the where clause uses an index on the first table, then the joins and where should all be using indexes, and the query will be quite fast.

MySql string manipulation, selecting items from text

I have a "changesets" table which has a comments column where people enter references to bug issues in the format "Fixed issue #2345 - ......", but can also be "Fixed issues #456, #2956, #12345 ...."
what's the best way to select these reference numbers so i can access the issues via a join.
given this change sets table
id comments
===========================
1 fixed issue #234 ....
2 DES - #789, #7895, #123
3 closed ticket #129
i'd like results like this
changeset_id issue_id
=====================
1 234
2 789
2 7895
2 123
3 129
I've used substring_index(substring_index('#',-1),' ',1) type construct but that will only return a single reference per line.
Also looking for the most efficient way to do this text lookup
Any help appreciated
Thanks
Here's one (bloated/messy) approach on how to get the desired dataset...
Step 1 - figure out what the maximum # of issue ids is
SELECT MAX(LENGTH(comments)- LENGTH(REPLACE(comments,'#',''))) AS max_issues
FROM change_sets
Step 2 - recursively create a UNION'd query with a number of "levels" equal to the maximum number of issue ids. For your example,
SELECT changeset_id, issue_id FROM
(
SELECT id AS changeset_id, CAST(SUBSTRING_INDEX(comments,'#',-1) AS UNSIGNED) AS issue_id FROM change_sets
UNION
SELECT id AS changeset_id, CAST(SUBSTRING_INDEX(comments,'#',-2) AS UNSIGNED) AS issue_id FROM change_sets
UNION
SELECT id AS changeset_id, CAST(SUBSTRING_INDEX(comments,'#',-3) AS UNSIGNED) AS issue_id FROM change_sets
) a
HAVING issue_id!=0
ORDER BY changeset_id, issue_id
I'm taking advangage of UNION's ability to remove duplicate rows, and CAST's ability to use the leading numeric values when deciding the integer.
The result using your toy dataset:
+--------------+----------+
| changeset_id | issue_id |
+--------------+----------+
| 1 | 234 |
| 2 | 123 |
| 2 | 789 |
| 2 | 7895 |
| 3 | 129 |
+--------------+----------+

MySQL - COUNT before INSERT in one query

Hey all, I am looking for a way to query my database table only once in order to add an item and also to check what last item count was so that i can use the next number.
strSQL = "SELECT * FROM productr"
After that code above, i add a few product values to a record like so:
ID | Product | Price | Description | Qty | DateSold | gcCode
--------------------------------------------------------------------------
5 | The Name 1 | 5.22 | Description 1 | 2 | 09/15/10 | na
6 | The Name 2 | 15.55 | Description 2 | 1 | 09/15/10 | 05648755
7 | The Name 3 | 1.10 | Description 3 | 1 | 09/15/10 | na
8 | The Name 4 | 0.24 | Description 4 | 21 | 09/15/10 | 658140
i need to count how many times it sees gcCode <> 'na' so that i can add a 1 so it will be unique. Currently i do not know how to do this without opening another database inside this one and doing something like this:
strSQL2 = "SELECT COUNT(gcCode) as gcCount FROM productr WHERE gcCode <> 'na'
But like i said above, i do not want to have to open another database query just to get a count.
Any help would be great! Thanks! :o)
There's no need to do everything in one query. If you're using InnoDB as a storage engine, you could wrap your COUNT query and your INSERT command in a single transaction to guarantee atomicity.
In addition, you should probably use NULL instead of na for fields with unknown or missing values.
They're two queries; one is a subset of the other which means getting what you want in a single query will be a hack I don't recommend:
SELECT p.*,
(SELECT COUNT(*)
FROM PRODUCTR
WHERE gccode != 'na') AS gcCount
FROM PRODUCTR p
This will return all the rows, as it did previously. But it will include an additional column, repeating the gcCount value for every row returned. It works, but it's redundant data...