MySQL: How to delete rows from the set? - mysql

I have a request of translation:
(table structure:
line_id
lang ("en" or "ru")
text
so, in the table it looks like:
5 | en | Test
6 | en | Hello!
7 | en | Another words
6 | ru | Привет!
and all translations are in the same table with key line_id, that is independent from the language)
SELECT txt.line_id, txt.text AS o, ru.text AS t
FROM text AS txt, text AS ru
WHERE txt.line_id = ru.line_id
AND txt.lang = 'en';
it will return such array
> [5] => Array ( [line_id] => 5 [o] => Test [t] => Test
> [6] => Array ( [line_id] => 6 [o] => Hello! [t] => Hello! )
> [7] => Array ( [line_id] => 6 [o] => Hello! [t] => Привет! )
o - is original text, t - translation.
How to delete from set #6 row, because we have translation in the next row. GROUP BY will kill #7 and save #6 row.
the best result would be:
> [5] => Array ( [line_id] => 5 [o] => Test [t] => )
> [6] => Array ( [line_id] => 6 [o] => Hello! [t] => Привет! )
without [o] => Hello! [t] => Hello!

I'm just guessing you probably need:
SELECT txt.line_id,
txt.text AS o,
COALESCE (ru.text,'UNTRANSLATED') AS t
FROM
text AS txt
LEFT JOIN
text AS ru
ON txt.line_id=ru.line_id AND ru.lang='ru'
WHERE txt.lang='en'

How about sort by lang desc (so ru is on top) and then group by line_id?

exclude the original language from the translation table
SELECT txt.line_id, txt.text AS o, ru.text AS t
FROM text AS txt, text AS ru
WHERE txt.line_id=ru.line_id
AND txt.lang='en'
AND ru.lang!='en'
new version for new description
SELECT txt.line_id, ru.lang, ru.text AS t
FROM text AS txt, text AS ru
WHERE txt.line_id=ru.line_id
AND txt.lang='en'
ORDER BY text.lang, txt.line_id

In order to list translated rows and original rows without translation, you must face the lack of support in MySql for sub queries that access the same table.
In my opinion you could divide the problem: first list original rows without translations with one SELECT; then list translations with another SELECT; in the end use a UNION.
-- original without translations
SELECT txt.line_id, txt.text AS o, ru.text AS t
FROM text AS txt LEFT JOIN text AS ru
ON(txt.line_id = ru.line_id AND txt.lang = 'en' AND ru.lang != 'en')
WHERE ru.line_id IS NULL -- ensures no translations have been found
UNION
-- only matching translations
SELECT txt.line_id, txt.text AS o, ru.text AS t
FROM text AS txt JOIN text AS ru
ON(txt.line_id = ru.line_id AND txt.lang = 'en' AND ru.lang != 'en');
Best regards.

Related

Creating specific size

I have a table in html.I want to add that table with php on my database. The problem is with my table. I have done that
CREATE TABLE `playersrb` (
`position` numeric(24) DEFAULT NULL,
`piece_color` enum('B','R') NOT NULL,
`id` numeric(30) DEFAULT NULL,
`last_action` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp()
)
I have 24 positions and that I want to give in my table that those 24 are that size,not 25 and going on..Second,I have B and R that it is the color (It is ok that).Now I put Id because that's how I name the images I have on my table (the table that it is in my html) ,I name those Id:0 ,Id:1 until 30 (because 30 are the max images I have - I don't want more).
I create with that my table,I open sqlite,I go to that table and I start putting
position piece_color id last_action
Null | R | 0 | here it was saying the time
Null | R | 1 | the time as previous
Null | B | 2 |
Null | B | 3 |
Null | R | 4 |
Null | R | 5 |
Null |B | 6 |
and it goes like this until the end of 30
.
.
.
.
.
I press save the button ,all fine.I go to phpmyadmin to check my table and it wasn't as I created..How can I do that thing?to have position 24 ,30 id (that will be different images ) .Save the 30 Id to 24 position.
edited: as you can see from the image I have created position and id .The table I want is like those. The position is where it belongs my image. The id is the image.I just want to pass from a table I have the position and the id in that table and I want to be right..When I move those images they create a table with variable position and Id. That table I want to pass into my table (in database table).If I put more images I will have Id 2 and the position which I dragged.That's what I am trying to do. As you can understand I want to have only 30 images.Every image is unique. They have other Id,not the same.More details,id =0 is the image a , the id =1 is the image b .The positions is ,in the image as you can see it is just the number of the table you see it where I move those images.
EDITED
<table class="content-table">
<tr>
<th>Image</th>
<th>Position(TO)</th>
<th>New Position</th>
</tr>
</div>
</div>
</div>
<?php
require_once "C/dbconnect.php";
$sql = "SELECT image, position,new_position FROM playersrb" ;
$sql="
SET #old_position = 1;
SET #new_position = 12;
SELECT image
, position old_order
, ROUND(CASE WHEN position NOT BETWEEN LEAST(#old_position,#new_position) AND GREATEST(#old_position,#new_position)
THEN position
WHEN position = #old_position THEN #new_position
ELSE position+(((#new_position<#old_position)-.1)*12)
END
,0) new_order
FROM playersrb;
";
$result = $mysqli-> query($sql);
if($result-> num_rows >0) {
while ($row = $result-> fetch_assoc()){
echo "<tr><td>". $row["image"] ."</td><td>". $row["position"] ."</td></tr>";
}
echo "</table>";
}
else{
echo "0 result";
}
$mysqli->close();
?>
</table>
I don't really understand your question, but here's an example of re-ordering a list...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(image CHAR(1) NOT NULL PRIMARY KEY
,position INT NOT NULL
);
INSERT INTO my_table VALUES
('A',1),
('B',2),
('C',3),
('D',4),
('E',5),
('F',6);
So, let's say we want to drag the image in position 5 to position 2...
SET #old_position = 5;
SET #new_position = 2;
SELECT image
, position old_order
, ROUND(CASE WHEN position NOT BETWEEN LEAST(#old_position,#new_position) AND GREATEST(#old_position,#new_position)
THEN position
WHEN position = #old_position THEN #new_position
ELSE position+(((#new_position<#old_position)-.5)*2)
END
,0) new_order
FROM my_table;
+-------+-----------+-----------+
| image | old_order | new_order |
+-------+-----------+-----------+
| A | 1 | 1 |
| B | 2 | 3 |
| C | 3 | 4 |
| D | 4 | 5 |
| E | 5 | 2 |
| F | 6 | 6 |
+-------+-----------+-----------+
Here's a fuller example, using some PHP to output to HTML... perhaps someone else can make it pretty...
<?php
//simple_sorter.php
//Preamble
/*
A simple row repositioning script.
This is using a simple $_GET to determine which row is repositioned.
So you need to supply a source and a target in the url, e.g.:
https://path/to/simple_sorter.php?old_position=5&new_position=2
There is no error checking, so it can quite easily fall apart, and because
the SELECT comes befpre the UPDATE, you won't see any changes until the
next time you load the page.
*/
//Data Creation Statements
/*
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(image CHAR(1) NOT NULL PRIMARY KEY
,position INT NOT NULL
);
INSERT INTO my_table VALUES
('A',1),
('B',2),
('C',3),
('D',4),
('E',5),
('F',6);
*/
require('path/to/pdo/connection/stateme.nts');
//My understanding is that the following is needed
in order to replace (every instance within the
query of) :old_position and :new_position with
their corresponding values
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, TRUE);
//and now to the code...
$query = "
SELECT *
FROM my_table
ORDER
BY position
";
$stmt = $pdo->prepare($query);
$stmt->execute();
$data = $stmt->fetchAll();
print_r($data);
$query = "
UPDATE my_table x
JOIN
( SELECT image
, position old_order
, ROUND(CASE WHEN position NOT BETWEEN LEAST(:old_position,:new_position) AND GREATEST(:old_position,:new_position)
THEN position
WHEN position = :old_position THEN :new_position
ELSE position+(((:old_position>:new_position)-.5)*2)
END
,0) new_order
FROM my_table
) y
ON y.image = x.image
SET position = new_order
";
$old_position = $_GET['old_position'];
$new_position = $_GET['new_position'];
$stmt = $pdo->prepare($query);
$stmt->execute(array('old_position' => $old_position,'new_position' => $new_position));
?>
Outputs (for instance, and depending what values were used, and how often)...
Array
(
[0] => Array
(
[image] => A
[position] => 1
)
[1] => Array
(
[image] => D
[position] => 2
)
[2] => Array
(
[image] => E
[position] => 3
)
[3] => Array
(
[image] => B
[position] => 4
)
[4] => Array
(
[image] => C
[position] => 5
)
[5] => Array
(
[image] => F
[position] => 6
)
)

MySQL match pattern and select number and letters

I have a list of IDs which are created in various third party applications systems and manually added to our system. I need to try and auto increment these IDs based on the largest number. The values are either entirely a number or any number of letters followed by any number of numbers.
For example:
Array ( [works_id] => MD001 [num] => 0 )
Array ( [works_id] => WX9834V [num] => 0 )
Array ( [works_id] => WK009 [num] => 0 )
Array ( [works_id] => W4KHA2 [num] => 0 )
Array ( [works_id] => MD001 [num] => 0 )
Array ( [works_id] => DE1234 [num] => 0 )
Array ( [works_id] => 99 [num] => 99 )
Array ( [works_id] => 100 [num] => 100 )
In the above example, I would need to return 'DE' and 1234 as 1234 is the largest number which matches the pattern (WX9834V does not match as it is LLNNNNL)
So far I have tried:
SELECT works_id, CAST(works_id as UNSIGNED) as num
FROM table
WHERE (works_id REGEXP '^[a-zA-Z]+[0-9]' or works_id REGEXP '^[0-9]+$')
But this returns all rows and returns 0 for the number part unless it is only made up of numbers - how can I return only 'DE' and 1234 from the above?
From the comments, I understant that your primary intent is to select the records that do match your format spec (possibly characters at the beginning of the string, then mandatory numbers until the end of string).
The problem with you current query is that the first regexp, '^[a-zA-Z]+[0-9]' is too permissive: it does allow non-numbers characters at the end of the field, and would be better written '^[a-zA-Z]+[0-9]+$'
Bottom line, the two regexes can be combined into one:
SELECT works_id
FROM mytable
WHERE works_id REGEXP '^[a-zA-Z]*[0-9]+$'
The regexp means:
^ beginning of the string
[a-zA-Z]* 0 to N letters
[0-9]+ at least one digit
$ end of string
In this db fiddle with your test data, this returns:
| works_id |
| -------- |
| MD001 |
| WK009 |
| MD001 |
| 99 |
| 100 |
NB : in MySQL pre-8.0, splitting the string in order to find the max numerical pain is hard to do, since functions such as REGEXP_REPLACE are not available. It is probably easier to do this in your application (unless you have a very large numbers of matching records...). You can have a look at this post or this other one for solutions that mostly rely on MySQL functions.

MySQL Order By - IN

SELECT
DISTINCT(p_title), title
FROM
`reg`
WHERE
( C_title REGEXP '[ENGINE]' or C_title REGEXP '[ENGINEER]')
AND title IN ('Prof. Dr.','Dr.','Mr.')
or
ORDER BY FIELD (title,'Prof. Dr.','Dr.')
I want to list only the Prof. Dr. and Dr. fields in my query. But first Prof's and second Dr's .... But, these codes are not functioning.
WHEN title = 'Prof. Dr.' THEN 1
WHEN title = 'Assoc. Prof. Dr.' THEN 2
WHEN title = 'Assist. Prof. Dr.' THEN 3
WHEN title = 'Dr.' THEN 4
WHEN title != 'Ms.' THEN 5
WHEN title != 'Mr.' THEN 6
ELSE 7
Could you tell me the alternative ways ?
Use an enumerated field containing all the possible values, for better index performance, smaller data amount and better less errors (e.g. someone missing the dot after "Mr.",
What you are looking for are stacked if-statements:
SELECT title, IF('title' = 'Mr.', 1,
IF (title = 'Mrs.', 2,
IF (title = 'Dr.', 3, 0)
)
)
AS title_number
FROM users...
will return
title | title_number
--------------------
"Mr." | 1
"xxx" | 0
...

Mysql Input/Output query

I have two querys:
SELECT LancamentoEntrada.*,
TipoEntrada.descricao AS nome,
Usuario.nome AS obreiro
FROM lancamento_entradas LancamentoEntrada,
tipo_entradas TipoEntrada,
obreiros Obreiro,
usuarios Usuario
WHERE LancamentoEntrada.tipo_entrada_id = TipoEntrada.id
AND TipoEntrada.somar_caixa = 1
AND LancamentoEntrada.obreiro_id = Obreiro.id
AND Usuario.id = Obreiro.usuario_id
AND LancamentoEntrada.data_entrada >= '{$begin}'
AND LancamentoEntrada.data_entrada <= '{$end}'
ORDER BY LancamentoEntrada.data_entrada
And
SELECT LancamentoSaida.*,
TipoSaida.descricao AS nome
FROM lancamento_saidas LancamentoSaida,
tipo_saidas TipoSaida
WHERE LancamentoSaida.tipo_saida_id = TipoSaida.id
AND TipoSaida.somar_caixa = 1
AND LancamentoSaida.data_saida >= '{$begin}'
AND LancamentoSaida.data_saida <= '{$end}'
ORDER BY LancamentoSaida.data_saida
Which generate the follow arrays:
// Query 1
Array(
[0] => Array (
[id] => 3
[tipo_entrada_id] => 1
[data_entrada] => 2012-05-08
[data_vencimento] => 2012-05-08
[obreiro_id] => 2
[valor_pago] => 20.00
[valor_pagar] => 0.01
[observacoes] => TESTE
)
[1] => Array (
[...]
)
)
// Query 2
Array (
[0] => Array (
[id] => 1
[tipo_saida_id] => 1
[data_saida] => 2012-05-08
[data_vencimento] => 2012-05-08
[valor_pago] => 200.00
[observacoes] => tESTE
)
[1] => Array (
[...]
)
)
But, I want to do one query, listing inputs and outputs, how I can acomplish this?
If need more explanation, please, ask-me.
EDIT 1
inputs are generated from first query, output from second.
EDIT 2
The querys need to generate report of financial input/output, so, the first query get all input stored and the second get all output generated, both betwenn from one period. I need to generate a list with all, input and output, ordered by date.
Edit 3
I have done this query, the problem is, how I know when is input and when is output?
Tried ISNULL and CASEs, but not work.
(SELECT LancamentoEntrada.data_entrada AS data,
LancamentoEntrada.data_vencimento AS vencimento,
LancamentoEntrada.valor_pago AS valor,
LancamentoEntrada.observacoes AS observacoes,
TipoEntrada.descricao AS nome
FROM lancamento_entradas LancamentoEntrada,
tipo_entradas TipoEntrada
WHERE LancamentoEntrada.tipo_entrada_id = TipoEntrada.id
AND TipoEntrada.somar_caixa = 1
)
UNION
(SELECT LancamentoSaida.data_saida AS data,
LancamentoSaida.data_vencimento AS vencimento,
LancamentoSaida.valor_pago AS valor,
LancamentoSaida.observacoes AS observacoes,
TipoSaida.descricao AS nome
FROM lancamento_saidas LancamentoSaida,
tipo_saidas TipoSaida
WHERE LancamentoSaida.tipo_saida_id = TipoSaida.id
AND TipoSaida.somar_caixa = 1
)
If the only thing you still need is to identify which records came from which query you just need to add a literal to each query.
( SELECT
'Input' as rec_type,
LancamentoEntrada.data_entrada AS data,
LancamentoEntrada.data_vencimento AS vencimento,
LancamentoEntrada.valor_pago AS valor,
LancamentoEntrada.observacoes AS observacoes,
TipoEntrada.descricao AS nome
FROM lancamento_entradas LancamentoEntrada,
tipo_entradas TipoEntrada
WHERE LancamentoEntrada.tipo_entrada_id = TipoEntrada.id
AND TipoEntrada.somar_caixa = 1
)
UNION ALL
(SELECT
'Output' as rec_type,
LancamentoSaida.data_saida AS data,
LancamentoSaida.data_vencimento AS vencimento,
LancamentoSaida.valor_pago AS valor,
LancamentoSaida.observacoes AS observacoes,
TipoSaida.descricao AS nome
FROM lancamento_saidas LancamentoSaida,
tipo_saidas TipoSaida
WHERE LancamentoSaida.tipo_saida_id = TipoSaida.id
AND TipoSaida.somar_caixa = 1
)
As an aside you'll get better performance if you UNION ALL Since UNION would remove duplicates from the two sets which you won't have in this case.

Perl (or R, or SQL): Count how often string appears across columns

I have a text file that looks like this:
gene1 gene2 gene3
a d c
b e d
c f g
d g
h
i
(Each column is a human gene, and each contains a variable number of proteins (strings, shown as letters here) that can bind to those genes).
What I want to do is count how many columns each string is represented in, output that number and all the column headers, like this:
a 1 gene1
b 1 gene1
c 2 gene1 gene3
d 3 gene1 gene2 gene3
e 1 gene2
f 1 gene2
g 2 gene2 gene3
h 1 gene2
i 1 gene2
I have been trying to figure out how to do this in Perl and R, but without success so far. Thanks for any help.
This solution seems like a bit of a hack, but it gives the desired output. It relies on using both plyr and reshape packages, though I'm sure you could find base R alternatives. The trick is that function melt lets us flatten the data out into a long format, which allows for easy(ish) manipulation from that point forward.
library(reshape)
library(plyr)
#Recreate your data
dat <- data.frame(gene1 = c(letters[1:4], NA, NA),
gene2 = letters[4:9],
gene3 = c("c", "d", "g", NA, NA, NA)
)
#Melt the data. You'll need to update this if you have more columns
dat.m <- melt(dat, measure.vars = 1:3)
#Tabulate counts
counts <- as.data.frame(table(dat.m$value))
#I'm not sure what to call this column since it's a smooshing of column names
otherColumn <- ddply(dat.m, "value", function(x) paste(x$variable, collapse = " "))
#Merge the two together. You could fix the column names above, or just deal with it here
merge(counts, otherColumn, by.x = "Var1", by.y = "value")
Gives:
> merge(counts, otherColumn, by.x = "Var1", by.y = "value")
Var1 Freq V1
1 a 1 gene1
2 b 1 gene1
3 c 2 gene1 gene3
4 d 3 gene1 gene2 gene3
....
In perl, assuming the proteins in each column don't have duplicates that need to be removed. (If they do, a hash of hashes should be used instead.)
use strict;
use warnings;
my $header = <>;
my %column_genes;
while ($header =~ /(\S+)/g) {
$column_genes{$-[1]} = "$1";
}
my %proteins;
while (my $line = <>) {
while ($line =~ /(\S+)/g) {
if (exists $column_genes{$-[1]}) {
push #{ $proteins{$1} }, $column_genes{$-[1]};
}
else {
warn "line $. column $-[1] unexpected protein $1 ignored\n";
}
}
}
for my $protein (sort keys %proteins) {
print join("\t",
$protein,
scalar #{ $proteins{$protein} },
join(' ', sort #{ $proteins{$protein} } )
), "\n";
}
Reads from stdin, writes to stdout.
A one liner (or rather 3 liner)
ddply(na.omit(melt(dat, m = 1:3)), .(value), summarize,
len = length(variable),
var = paste(variable, collapse = " "))
If it's not a lot of columns, you can do something like this in sql. You basically flatten out the data into a 2 column derived table of protein/gene and then summarize it as needed.
;with cte as (
select gene1 as protein, 'gene1' as gene
union select gene2 as protein, 'gene2' as gene
union select gene3 as protein, 'gene3' as gene
)
select protein, count(*) as cnt, group_concat(gene) as gene
from cte
group by protein
In mysql, like so:
select protein, count(*), group_concat(gene order by gene separator ' ') from gene_protein group by protein;
assuming data like:
create table gene_protein (gene varchar(255) not null, protein varchar(255) not null);
insert into gene_protein values ('gene1','a'),('gene1','b'),('gene1','c'),('gene1','d');
insert into gene_protein values ('gene2','d'),('gene2','e'),('gene2','f'),('gene2','g'),('gene2','h'),('gene2','i');
insert into gene_protein values ('gene3','c'),('gene3','d'),('gene3','g');