I want to create a mysql table with three columns id , name, name_id,
The thing that i want to acheive is whenever user enters a name into database
then system should generate a unique id for name automatically.
e.g.
name is JJ then name_id should be 1 and if name is DD then name_id should be 2
also if name JJ is repeated in database then name_id should be 1.
The number_id values should be assign according to name sorting
i.e A should get 1 and B should get 2.
How this can be achieved by sql script or triggers ?
What about the following?
INSERT INTO tbl (name,name_id)
SELECT newname, COALESCE((SELECT name_id FROM tbl WHERE name=newname ),
(SELECT max(name_id)+1 FROM tbl))
This is assuming that column id takes care of itself, i.e. is auto_incremented.
newname can of course also be a string constant which you will have to work in to your command.
The command above works best when used for indiviual inserts ("by a user"). If you want to carry out a bulk import then it can be quite costly since for each new value the table tbl will be scanned twice. For this case a different logic should be applied:
First find all name-name_id pairs by means of a grouped select and then INNER JOIN the results with the import list. For the remaining items (without existing name_ids) do the following: find out the highest #i=max(name_id) of all records and then import the sorted list with an autonumbering mechanism (#i:=#i+1) for name_id in place ...
create a sql function that returns the name
_id upon passing name as a parameter. one way would be to add all the characters but that wont do because different arrangement of same characters would give the same sum for different names.may be concatenating primary index at the end of sum would do the job. i think you can define a suitable logic in a sql function to achieve the results.
Related
My source task has following columns read from csv file:
Sale_id, sale_date, order_no, sale_amt
This is followed by lookup task that looks into the sales sql table (having same column names) and the join is on order_no column.
The issue is that order_no data in sql sale table has value like 'ABC-12345', 'WXYZ-32111' (there are couple of characters prepended to the order number).
Where as in the csv there is '12345' without any characters prepended.
Hence I cannot do a lookup as there is no direct match. Is there any way to remove the characters and the hyphen from sale sql table data (temporarily) for performing the lookup join.
1st Data Flow Task - Use Flat File Source to LookUp and OLE DB Source via SQL Command with the following to LookUp.
select Sale_id, sale_date, order_no, sale_amt,
substring(order_no,charindex('-',order_no,0),len(order_no)) as [key]
from sql sale table
Use [Key] for your look up transformation.
The functions should provide the numeric values you are looking for.
Problem restatement
In your source, you have order numbers coming in that look like numbers. You need to be able to lookup against a source that has a text string prepended to an order number. We can assume the numeric part of the database's order number is unique.
Setup
I created a simplified version of your table and populated it with data
DROP TABLE IF EXISTS dbo.so_66446302;
CREATE TABLE dbo.so_66446302
(
sales_id int
, order_no varchar(20)
);
INSERT INTO dbo.so_66446302
(sales_id, order_no)
VALUES
(1, 'ABC-12345')
, (2, 'WXYZ-32111')
, (3, 'REALLY_LONG-654321');
A critical piece in using Lookup components is getting the data types to agree. I'm going to assume that the order number from the file is a DT_STR and not an integer.
By default, people pick a table in the Lookup component's Connection tab, dbo.so_66446302 but if you check "Use results of a SQL query", you'll have what you're looking for.
Similar query to what Jatin shows below but I find "showing my work" along the way helps me debug when it goes sideways. In this query, that's why I have the intermediate cross apply steps.
SELECT
S.*
, D0.order_no_as_string_digits
FROM
dbo.so_66446302 AS S
CROSS APPLY
(
SELECT
-- Length of the string less where we find the first dash
LEN(S.order_no) - CHARINDEX('-', S.order_no, 1)
)D(dash_location)
CROSS APPLY
(
SELECT RIGHT(S.order_no, D.dash_location)
)D0 (order_no_as_string_digits);
The results of that query are
sales_id order_no order_no_as_string_digits
1 ABC-12345 12345
2 WXYZ-32111 32111
3 REALLY_LONG-654321 654321
Now you can match the derived order number in the database to the one in your file by dragging the columns together. Check any/all columns that you need to retrieve from the database and send the data to the intended destination.
I'm new to MySQL and I currently have following problem:
I have a table "offers" where a user can place an offer to another user where he offers him ingame items. The items are placed into the datafield with the ItemID and ItemLevel separated by a ":" and the itemes by a ",".
Example: "0:346,2:638,1:646" = offers Item with ID 0 Level 346 and ID 2 Level 638 and ID 1 Level 646
Now I want to create a query with PHP to SELECT only offers with specific IDs and a range of Level.
Should I rebuild the whole thing and do it with 2 different tables "orders" and "order_data" or is it possible to make this filtering possible by a simple query?
You must normalize your data... As you say, you should distribute the orders data between multiple tables and columns.
However, there's a possible (and simple) solution for your request:
SELECT * FROM orders
WHERE 1=1
AND FIND_IN_SET('0:346', orderData)
;
FIND_IN_SET function returns the index of the searched string - if it exists on column - so it's usually used as a SELECT field in order to know string's index on comma separated string. But, as in this case, you can use it on WHERE clause in order to return only the records that contains the specific string.
I have a table in which there are a listing of names, first and last in a column. So a column, called "manager" could have a value of "John Doe". I want to right a query that simply goes through each row in this table and displays the first letter and last name of the "manager" column. Everything I do comes up with "Subquery returns more than one row".
Starting small, I've just decided to pull the first letter:
SELECT id, LEFT((SELECT manager FROM my_table), 1) FROM my_table;
Or am I just completely off base on this
You're using a subquery to fetch into a field of a parent query. As such, the subquery can return only a single row. think of it this way: a result set is a 2-dimensional construct. a series of columns and rows. The data a subquery returns has to match the physical constraints of the thing it's returning into.
Since you're fetching into a field, that means one SINGLE value. If multiple values were allowed to be returned, you'd effectively be trying to turn your 2D result set into a 3d set (rows + columns plus a skyscraper growing out of one of those fields).
Your query does NOT need to be a subquery at all:
SELECT id, LEFT(manager, 1) AS first_letter FROM yourtable
Also, not that if you want separate first and last names, you would be better off storing those are separate fields. It is very easy to rebuilt a name from a set of first/last name fields, but very very difficult to reliably separate a monolithic name into individual first and last names, e.g.
simple:
John Doe (fn: john, ln: doe)
hard:
Billy Jo Todd (is that "Billy" and "Jo Todd", "Billy" and "Todd" with middle name Jo?
dead simple:
field firstname = John
field lastname = Doe
If you want to use a subquery, this query works as you intend it to, though I am not sure this is the best way to proceed in any case. We would need more information about your needs to assert that.
SELECT
m1.id,
m2.manager
FROM
my_table AS m1 INNER JOIN
(SELECT id, LEFT(manager, 1) AS manager FROM my_table) as m2
ON m1.id = m2.id
http://sqlfiddle.com/#!2/9c395/6
You must add a condition to your subquery to return the row you want to compare like
SELECT manager FROM my_table WHERE id = 1
I have a mysql table in which there is a column e.g. called name. The column data has a specific pattern nameBase+number. E.g.
name
----------
test0
test1
test2
stack0
stack1
stack2
Each time I want to add data to the column, I have to find the last number for specific nambeBase and add the new entry +number+1.
For example, if now test came, I have to add test3 to db.
My question: What is the best way to 1. check if the nameBase already exists in db(sth like contains) and 2.find the last nameBase number. E.g. here for test is 3.
Update : Everyone, one update. I finally used java Pattern class. So cool and easy. It made everything so simple. I just could add the /d to pattern and then I could check if that matches the name and could use the pattern group to easily access the second part.
The real solution here is to change the database schema to split this into two columns, the name and its number. It becomes trivial then to get the aggregate MAX() via
SELECT name, MAX(num) AS num FROM tbl GROUP BY name
However,if changing it is not an option, I would recommend using REPLACE() to remove the name portion from the column value leaving only the number portion when querying, and get the aggregate MAX() of that to find the highest existing number for it:
SELECT
MAX(REPLACE(name, <the name to search>, '')) AS maxnum
FROM tbl
WHERE
name LIKE '<the name to search>%'
Or instead of LIKE, using a regular expression, which is more accurate than LIKE (in case a name contains another name, the LIKE might match) but more expensive:
SELECT
MAX(REPLACE(name, <the name to search>, '')) AS maxnum
FROM tbl
WHERE
name REGEXP '^<the name to search>[0-9]+$'
I would do this with an additional table with two columns and store in this table each name and the last assigned id. And then replace your nameBase+number column in your original table with a name column being a foreign key to the addition table, and a number column, being the appropriate count for that entry.
This will be much easier and more efficient to manipulate.
If possible, I would restructure the table to place these in either 2 tables (better) or at least two columns (medium). The structure you have is not normalized at all :-/
Without knowing too much about your schema; here is my recommendation for the two-table solution: (note: this is normalized and also follows the idiom "Do not store that which can be calculated")
names
------
id | name
01 | test
02 | stack
name_hits
-------
name_id | date
01 | 01/01/2001
01 | 01/15/2001
01 | 04/03/2001
02 | 01/01/2001
...
and then select like this:
SELECT names.name, count(name_hits.id) as hits
FROM names JOIN name_hits ON names.id=name_hits.name_id
GROUP BY names.id
and insert like this:
INSERT INTO name_hits SELECT id, NOW() FROM names WHERE name = "stack";
Presuming that you are unable to change the structure of the table, you can do what you want. However, it is rather expensive.
What you would like to do is something like:
select name
from t
where left(name, length(<name parameter>)) = <name parameter>
order by name desc
limit 1
Unfortunately, your naming probably does not allow this, because you are not left padding the numeric portion with zeroes.
So, the following gets around this:
select name,
cast(substring(name, length(<name parameter>), 1000) as int) as number
from t
where left(name, length(<name parameter>)) = <name parameter>
order by 2 desc
limit 1
This is not particularly efficient. Also, indexes cannot really help with this because the collating sequence for strings is different than for numbers (test0, test1, test10, test100, test11, etc. versus 0, 1, 2, 3, 4 . . .).
If you can, I would follow the advice of the others who suggest multiple columns or tables. I only offer this as a method where you don't have to modify the current table.
If you cannot change the schema, try this:
INSERT INTO names (name)
SELECT CONCAT("stack", CAST(TRIM(LEADING "stack" FROM name) AS INT)+1)
WHERE name LIKE "stack%" ORDER BY name DESC LIMIT 1;
The idea is:
select the "highest" previous value,
chop of the name,
cast the remaining string as an int,
add one to it,
then put the name back on it.
I have not tested this... I hope it leads you in the right direction.
Note that I have used a constant string "stack" as an example, you will likely want to make that dynamic.
I have the following table my_table with primary key id set to AUTO_INCREMENT.
id group_id data_column
1 1 'data_1a'
2 2 'data_2a'
3 2 'data_2b'
I am stuck trying to build a query that will take an array of data, say ['data_3a', 'data_3b'], and appropriately increment the group_id to yield:
id group_id data_column
1 1 'data_1a'
2 2 'data_2a'
3 2 'data_2b'
4 3 'data_3a'
5 3 'data_3b'
I think it would be easy to do using a WITH clause, but this is not supported in MySQL. I am very new to SQL, so maybe I am organizing my data the wrong way? (A group is supposed to represent a group of files that were uploaded together via a form. Each row is a single file and the the data column stores its path).
The "Psuedo SQL" code I had in mind was:
INSERT INTO my_table (group_id, data_column)
VALUES ($NEXT_GROUP_ID, 'data_3a'), ($NEXT_GROUP_ID, 'data_3b')
LETTING $NEXT_GROUP_ID = (SELECT MAX(group_id) + 1 FROM my_table)
where the made up 'LETTING' clause would only evaluate once at the beginning of the query.
You can start a transaction do a select max(group_id)+1, and then do the inserts. Or even by locking the table so others can't change (insert) to it would be possible
I would rather have a seperate table for the groups if a group represents files which belong together, especially when you maybe want to save meta data about this group (like the uploading user, the date etc.). Otherwise (in this case) you would get redundant data (which is bad – most of the time).
Alternatively, MySQL does have something like variables. Check out http://dev.mysql.com/doc/refman/5.1/en/set-statement.html