What I am trying to do is the following: I have a database with 3 cols that together form a unique combination. This combination I extracted to a new table (table 1). Now I would like to match the data that is present in seperate columns to the unique 3 col combination. For example:
cat dog day twenty two
cat dog day twenty eleven
cat dog morning eleven ten
should become
cat dog day "twenty=two&twenty=eleven"
cat dog morning "eleven=ten"
As an extra comment I should add that I am unable to predict how many items should be concatenated.
I tried the following: The string field should be updated with a concat of all results of the unique combination of val3,val4 and val5.
UPDATE `db`.`table1` , `db`.`table2`
SET
string =
(
SELECT group_concat(value1,'=',value2,'&') from table2
group by (val3,val4,val5)
)
WHERE
(
`table1`.`val3`=`table2`.`val3` AND
`table1`.`val4=table2`.`val4` AND
`table1`.`val5=table2`.`val5`)
;
A hint or tip would be much appreciated. Thansks in advance.
For reference : Solution does not work for me since I'm working with mySQL and I need to match 2 col's together.
Given this data structure:
CREATE TABLE `dummy` (
`col1` varchar(20),
`col2` varchar(20),
`key` varchar(20) DEFAULT NULL,
`val` varchar(20) DEFAULT NULL
);
The following query will give you what you are asking for:
select col1, col2, group_concat(concat(`key`,'=', `val`) SEPARATOR '&') from dummy group by col1, col2
Related
I need to find a record who dont have a specific value in CSV column. below is the table structure
CREATE TABLE `employee` (
`id` int NOT NULL AUTO_INCREMENT,
`first_name` varchar(100) NOT NULL,
`last_name` varchar(100) NOT NULL,
`keywords` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Sample record1: 100, Sam, Thompson, "50,51,52,53"
Sample record2: 100, Wan, Thompson, "50,52,53"
Sample record3: 100, Kan, Thompson, "53,52,50"
50 = sports
51 = cricket
52 = soccer
53 = baseball
i need to find the employees name who has the tags of "sports,soccer,baseball" excluding cricket
so the result should return only 2nd and 3rd record in this example as they dont have 51(cricket) but all other 3 though in diff pattern.
My query is below, but i couldnt get it worked any more.
SELECT t.first_name,FROM `User` `t` WHERE (keywords like '50,52,53') LIMIT 10
is there anything like unlike option? i am confused how to get this worked.
You could use FIND_IN_SET:
SELECT t.first_name
FROM `User` `t`
WHERE FIND_IN_SET('50', `keywords`) > 0
AND FIND_IN_SET('52', `keywords`) > 0
AND FIND_IN_SET('53', `keywords`) > 0
AND FIND_IN_SET('51', `keywords`) = 0;
Keep in mind it could be slow. The correct way is to normalize your table structure.
FIND_IN_SET will do the job for you but it does not use indexes. This is not a bug it's a feature.
SUBSTRING_INDEX can use an index and return the data as you wish. You don't have an index on it at the moment, But the catch here is that TEXT fields cannot be fully indexed and what you have is a TEXT field.
Normalize!
This is what you really should be doing. It's not a good idea to store comma separated values in a database. You really should be having a keywords table and since the keywords will be short, you can have a char or varchar narrow column which can be fully indexed.
I've got a SQL 2008 R2 table defined like this:
CREATE TABLE [dbo].[Search_Name](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](300) NULL),
CONSTRAINT [PK_Search_Name] PRIMARY KEY CLUSTERED ([Id] ASC))
Performance querying the Name field using CONTAINS and FREETEXT works well.
However, I'm trying to keep the values of my Name column unique. Searching for an existing entry in the Name column is unbelievably slow for a large number of names (usually batches of 1,000), even with an index on the Name field. Query plans indicate I'm using the index as expected.
To search for an existing value, my query looks like this:
SELECT TOP 1 Id, Name from Search_Name where Name = 'My Name Value'
I've tried duplicating the Name column to another column and searching on the new column, but the net effect was the same.
At this point, I'm thinking I must be mis-using this feature.
Should I just stop trying to prevent duplication? I'm using a linking table to join these search name values to the underlying data. It seems somehow 'dirty' to just store a whole bunch of duplicate values...
...or is there faster way to take a list of 1,000 names and see which ones are already stored in the database?
The first change to make is to get the entire list to SQL Server at one time. Regardless of how you add the names to the existing table, doing it as a set operation will make a big difference in performance.
Passing the List as a table-valued parameter (TVP) is a clean way to handle it. Have a look here for an example. You can still use an OUTPUT clause to track which rows did or didn't make the cut, for example:
-- Some sample existing names.
declare #Search_Name as Table ( Id Int Identity, Name VarChar(32) );
insert into #Search_Name ( Name ) values ( 'Bob' ), ( 'Carol' ), ( 'Ted' ), ( 'Alice' );
select * from #Search_Name;
-- Some (prospective) new names.
declare #New_Names as Table ( Name VarChar(32) );
insert into #New_Names ( Name ) values ( 'Ralph' ), ( 'Alice' ), ( 'Ed' ), ( 'Trixie' );
select * from #New_Names;
-- Add the unique new names.
declare #Inserted as Table ( Id Int, Name VarChar(32) );
insert into #Search_Name
output inserted.Id, inserted.Name into #Inserted
select New.Name
from #New_Names as New left outer join
#Search_Name as Old on Old.Name = New.Name
where Old.Id is NULL;
-- Results.
select * from #Search_Name;
-- The names that were added and their id's.
select * from #Inserted;
-- The names that were not added.
select New.Name
from #New_Names as New left outer join
#Inserted as I on I.Name = New.Name
where I.Id is NULL;
Alternatively, you could use a MERGE statement and OUTPUT the names that were added, those that weren't, or both.
Please look at the following staging table. It has multiple rows for the same policy.
Data to this table is loaded from a flat file I receive from external sources.
Column values can change between one row to the next row. See ColA. There could be limited columns populated in the first row. More columns will be populated in the next rows. See columns ColB and ColC, they are null initially and are populated in second and third rows.
`CREATE TABLE #Stg
(
PolicyNum VARCHAR(10) ,
ColA VARCHAR(10) ,
ColB VARCHAR(10) ,
ColC VARCHAR(10) ,
TimeStampKey VARCHAR(100)
)
INSERT #Stg
( PolicyNum, ColA, ColB, ColC, TimeStampKey )
VALUES ( 'MDT1000', 'SomeVal_A1', NULL, NULL, '2013041113033140MDT1000ZA' )
,
( 'MDT1000', 'SomeVal_A2', 'SomeVal_B', NULL, '2013041113051756MDT1000ZA' )
,
( 'MDT1000', 'SomeVal_A3', 'SomeVal_B', 'SomeVal_C', '2013041113115418MDT1000ZA' )`
From this staging table I need to load data to a destination table while maintaing history. Destination table is basically a type 2 slowly chaning dimension. In other words, I've load the first row from staging because it doesn't exist and update it with the second row and update again with the third row.
Folliwing is an example of destination schema:
CREATE TABLE #Dst
(
PolicyKey INT IDENTITY(1,1) PRIMARY KEY
, PolicyNum VARCHAR(10)
, ColA VARCHAR(10)
, ColB VARCHAR(10)
, ColC VARCHAR(10)
, IsActive BIT
, RowStartDate DATETIME
, RowEndDate DATETIME
)
Normally I'd write a merge statement or an SSIS package to handle incremental loads and scd dimensions, but since original record and change records are in the same file the standard approach doesn't work.
I'd appreciate if you can throw some light on how to approach this. I'm trying to avoid row by row operations.
Thanks,
Sam.
try this:
SELECT
Stg.*
FROM Stg
INNER JOIN
(
SELECT PolicyNum, MAX (TimeStampKey) AS MAX_TimeStampKey
FROM Stg
GROUP BY PolicyNum
) T
ON T.PolicyNum = Stg.PolicyNum
AND T.MAX_TimeStampKey = Stg.TimeStampKey
The result:
PolicyNum ColA ColB ColC TimeStampKey
---------- ---------- ---------- ---------- -------------------
MDT1000 SomeVal_A3 SomeVal_B SomeVal_C 2013041113115418MDT1000ZA
Please let us know if this helped you.
I have two mysql tables:
table 1:
id name type
1 a 123
2 b 125
table 2:
id text
1 test1
2 test2
these two tables need to be merged into a third table
table3:
id name type text
The id is an auto increment id. the first two tables have data that are not related. I mean, row for id=1 in table 1 has nothing to do with the row for id=1 in table two. So, I basically want to write a sql script which would insert values into table 3 to look like this in the end:
table3:
id name type text
1 a 123
2 b 125
3 test1
4 test2
the ids in the old tables and the new table don't have to match. Just the data from the tables need to be in the new table.
I am very new to mysql and if anyone can help me with this, it would be great!
thanks!
It can be done with something like this:
CREATE TABLE Table3 (
id int auto_increment,
name ...,
type int,
text ...,
PRIMARY KEY (id)
);
INSERT INTO table3 (name, type, text)
SELECT name, type, text FROM (
SELECT name, type, NULL AS text FROM table1
UNION ALL
SELECT NULL as name, NULL as type, text FROM table2) AS t
With auto-increment, we don't need to recount id at all.
Here's an SQL Fiddle for you to play with. )
I actually didn't understand what empty space in your scheme was for, and assumed it's all NULLs. If not, you can just replace NULL in this query with whatever default values you'd like.
Since nothing's related, start with #raina77ow's table, but just use two queries:
INSERT INTO table3 (name, type, text)
SELECT name, type, NULL
from table1;
INSERT INTO table3 (name, type, text)
SELECT NULL, NULL, text
from table2;
Simply Asking, Is there any function available in mysql to split single row elements in to multiple columns ?
I have a table row with the fields, user_id, user_name, user_location.
In this a user can add multiple locations. I am imploding the locations and storing it in a table as a single row using php.
When i am showing the user records in a grid view, I am getting problem for pagination as i am showing the records by splitting the user_locations. So I need to split the user_locations ( single row to multiple columns).
Is there any function available in mysql to split and count the records by character ( % ).
For Example the user_location having US%UK%JAPAN%CANADA
How can i split this record in to 4 columns.
I need to check for the count values (4) also. thanks in advance.
First normalize the string, removing empty locations and making sure there's a % at the end:
select replace(concat(user_location,'%'),'%%','%') as str
from YourTable where user_id = 1
Then we can count the number of entries with a trick. Replace '%' with '% ', and count the number of spaces added to the string. For example:
select length(replace(str, '%', '% ')) - length(str)
as LocationCount
from (
select replace(concat(user_location,'%'),'%%','%') as str
from YourTable where user_id = 1
) normalized
Using substring_index, we can add columns for a number of locations:
select length(replace(str, '%', '% ')) - length(str)
as LocationCount
, substring_index(substring_index(str,'%',1),'%',-1) as Loc1
, substring_index(substring_index(str,'%',2),'%',-1) as Loc2
, substring_index(substring_index(str,'%',3),'%',-1) as Loc3
from (
select replace(concat(user_location,'%'),'%%','%') as str
from YourTable where user_id = 1
) normalized
For your example US%UK%JAPAN%CANADA, this prints:
LocationCount Loc1 Loc2 Loc3
4 US UK JAPAN
So you see it can be done, but parsing strings isn't one of SQL's strengths.
The "right thing" would be splitting the locations off to another table and establish a many-to-many relationship between them.
create table users (
id int not null auto_increment primary key,
name varchar(64)
)
create table locations (
id int not null auto_increment primary key,
name varchar(64)
)
create table users_locations (
id int not null auto_increment primary key,
user_id int not null,
location_id int not null,
unique index user_location_unique_together (user_id, location_id)
)
Then, ensure referential integrity either using foreign keys (and InnoDB engine) or triggers.
this should do it
DELIMITER $$
DROP PROCEDURE IF EXISTS `CSV2LST`$$
CREATE DEFINER=`root`#`%` PROCEDURE `CSV2LST`(IN csv_ TEXT)
BEGIN
SET #s=CONCAT('select \"',REPLACE(csv_,',','\" union select \"'),'\";');
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;
You should do this in your client application, not on the database.
When you make a SQL query you must statically specify the columns you want to get, that is, you tell the DB the columns you want in your resultset BEFORE executing it. For instance, if you have a datetime stored, you may do something like select month(birthday), select year(birthday) from ..., so in this case we split the column birthday into 2 other columns, but it is specified in the query what columns we will have.
In your case, you would have to get exactly that US%UK%JAPAN%CANADA string from the database, and then you split it later in your software, i.e.
/* get data from database */
/* ... */
$user_location = ... /* extract the field from the resultset */
$user_locations = explode("%", $user_location);
This is a bad design, If you can change it, store the data in 2 tables:
table users: id, name, surname ...
table users_location: user_id (fk), location
users_location would have a foreign key to users thorugh user_id field