TypeORM multiple columns in IN clause - typeorm

In mysql,
select * from table where (column1, column2) IN (('1', '1'), ('2', '2'))
How can I use this in TypeORM.
I tried do this:
Like
const conditions = [('1', '1'), ('2', '2')];
QueryBuilder
.where(`(column1, column2) IN (${conditions.join(',')})`)
But this is not a good way, is there a better solution?

Related

Converting a SQL statement to rails command

I have a situation where I need to fetch only few records from a particular active record query response.
#annotations = Annotations.select('*', ROW_NUMBER() OVER (PARTITION BY column_a) ORDER BY column_b)
Above is the query for which the #annotations is the Active Record Response on which I would like to apply the below logic. Is there a better way to write the below logic in rails way?
with some_table as
(
select *, row_number() over (partition by column_a order by column_b) rn
from the_table
)
select * from some_table
where (column_a = 'ABC' and rn <= 10) or (column_b <> 'AAA')
ActiveRecord does not provide CTEs in its high level API; however with a little Arel we can make this a sub query in the FROM clause
annotations_table = Annotation.arel_table
sub_query = annotations_table.project(
Arel.star,
Arel.sql('row_number() over (partition by column_a order by column_b)').as(:rn)
)
query = Arel::Nodes::As.new(sub_query, Arel.sql(annotations_table.name))
Annotation.from(query).where(
annotations_table[:column_a].eq('ABC').and(
annotations_table[:rn].lt(10)
).or(annotations_table[:column_b].not_eq('AAA'))
)
The result will be a collection of Annotation objects using your CTE and the filters you described.
SQL:
select annotations.*
from (
select *, row_number() over (partition by column_a order by column_b) AS rn
from annotations
) AS annotations
where (annotations.column_a = 'ABC' and annotations.rn <= 10) or (annotations.column_b <> 'AAA')
Notes:
With a little extra work we could make this a CTE but it does not seem needed in this case
We could use a bunch of hacks to transition this row_number() over (partition by column_a order by column_b) to Arel as well but it did not seem pertinent to the question.

How can I do the equivalent to this mysql in rails

My query is mysql is as follows:
SELECT SUM(nbdownloaded) FROM (
SELECT field1, field2, nbdownloaded
FROM mytable
Where ....
) as reports
In rails I have the result of the subquery in variable.
Say for example I have:
my_reports = Reports.group("id").select("field1, field2, sum(field3) as nbdownloaded").where(...)")
And I want to have the sum of the field nbdownloaded.
Thank you
it's quite easy in rails active record query,
Model.sum(:nbdownloaded).where(:column =>'your condition')

Generic procedure to delete duplicates - no PKs

I wrote a stored procedure with a table name as parameter, that checks if there are duplicate rows in this table. The statements are built dynamically of course:
INSERT INTO tmpTable
SELECT col1, col2,... FROM table GROUP BY col1, col2, ... HAVING COUNT(*) > 1;
DELETE FROM tablename FROM tablenname
INNER JOIN tmpTable ON ISNULL(tablename.col1, 0) = ISNULL(tmpTable.col1, 0)
AND ISNULL(tablename.col2, 0) = ISNULL(tmpTable.col2, 0)
AND ...;
INSERT INTO tablename SELECT * FROM tmpTable;
Should work so far, but problem is, that it fails when the table has blob columns, like text. Those can not be compared in the JOIN. I also tried
DELETE FROM tablename GROUP BY col1, col2, ... HAVING COUNT(*) > 1;
but GROUP BY is not supported in DELETE statement directly without self-joining.
Also it's not possible to query information_schema for primary key of this table, since none of these tables has one.
Any ideas? Thanks.
Since the statement is already built dynamically, add casting the relevant columns to varchar(max) for the purpose of join. It's not difficult to figure which columns that are:
select c.name, quotename(c.name, '[')
from
sys.columns c
inner join sys.types t on c.system_type_id = t.system_type_id
where
c.object_id = object_id(#TABLE_NAME)
and c.is_computed = 0
and t.name in ('text', 'image', 'timestamp', 'xml')

Nested Select with ZF2

Trying to get a nested select using Zend\Db\Sql\Select and can't see anything at all in the documentation or on google.
Wanting to do something like this:
SELECT
table1.*,
(SELECT x,y,z FROM table2 WHERE table2.a = table1.a) as b
FROM table1
Without the nested select, it would look something like this:
$select = new Zend\Db\Sql\Select;
$select
->columns(array(
'*'
))
->from('table1')
ZF1 looked about creating a subSelect item and then adding it as an Expression inside the list of columns but in ZF2 it complains about an Expression needing to be a string.
Edit: The nested-select needs to be as a column as I end up with multiplied rows when using GROUP BY on same column name. This is the correct query I'm trying to get into Zend\Db\Sql\Select:
SELECT
users.id,
(SELECT count(explorations.id) FROM explorations WHERE user_id = users.id) as total_explorations,
count(villages.id)
FROM
users
INNER JOIN
villages
on (villages.user_id = users.id)
GROUP BY
users.id
Ralph Schindler has a repository of different DB patterns that he has specifically implemented in Zend\Db. Here's one for subselects: https://github.com/ralphschindler/Zend_Db-Examples/blob/master/example-20.php
The content is this:
<?php
/** #var $adapter Zend\Db\Adapter\Adapter */
$adapter = include ((file_exists('bootstrap.php')) ? 'bootstrap.php' : 'bootstrap.dist.php');
refresh_data($adapter);
use Zend\Db\Sql;
use Zend\Db\ResultSet\ResultSet;
$sql = new Sql\Sql($adapter);
$subselect = $sql->select();
$subselect->from('artist')
->columns(array('name'))
->join('album', 'artist.id = album.artist_id', array())
->where->greaterThan('release_date', '2005-01-01');
$select = $sql->select();
$select->from('artist')
->order(array('name' => Sql\Select::ORDER_ASCENDING))
->where
->like('name', 'L%')
->AND->in('name', $subselect);
$statement = $sql->prepareStatementForSqlObject($select);
$result = $statement->execute();
$rows = array_values(iterator_to_array($result));
assert_example_works(
count($rows) == 2
&& $rows[0]['name'] == 'Lady Gaga'
&& $rows[1]['name'] == 'Linkin Park'
);
Basically, you can use one select as the value of the predicate of another select.
I would suggest you restructure you SQL query. I'm not sure which database you are using, but if you are using MySQL, the COUNT function can use the DISTINCT keyword. This way you don't count the duplicated ids. I've adjusted your SQL query to what I would use, this way you eliminate the need for inner select.
SELECT
users.id,
COUNT(DISTINCT explorations.id) AS total_explorations,
COUNT(DISTINCT villages.id) AS total_villages
FROM users
INNER JOIN villages ON villages.user_id = users.id
INNER JOIN explorations ON explorations.user_id = users.id
GROUP BY users.id
I haven't run this query, but I'm sure it should work and give you the result you want. Hopefully I'm not misunderstanding your situation. Below is the equivalent Zend Framework 2 select.
$select = $sql->select('users');
$select->columns(array('id'));
$select->join('villages',
'villages.user_id = users.id',
array(
'total_villages' => new Expression("COUNT(DISTINCT villages.id)")
)
);
$select->join('explorations',
'explorations.user_id = users.id',
array(
'total_explorations' => new Expression("COUNT(DISTINCT explorations.id)")
)
);
What you are describing is defined as a JOIN. There are some different join scenarios and i will not cover the differences of them, but the most commons would be INNER JOIN or LEFT JOIN.
And this is indeed to be found inside the ZF2-Documentation of Zend\Db\Sql#Join
The Query would look like this:
Select
t1.*,
t2.field1,
t2.field2,
t2.field3
FROM
tablename1 t1,
tablename2 t2
WHERE
t1.field = t2.field
Looking at the Documentation of ZF2-Documentation of Zend\Db\Sql#Join, i think the Select would look like this:
$select = new \Zend\Db\Sql\Select();
$select->columns(array(
'id',
'title',
// List ALL Columns from TABLE 1 - * is bad/slow!
), true)->from(array(
't1' => 'tablename1'
))->join(
'tablename2',
'id = t1.id',
array(
'username',
'email',
// List ALL Columns from TABLE 2 u want to select
),
$select::JOIN_INNER
)
Another i think: If you don't use the columns() you'd SELECT * but for your own sake, start writing good queries ;) Have more control over your code!
Can't promise that this code works, since i don't use Zend\Db on my own, but using the Documentation on the right point should get you running nonetheless.
I Hope I am getting your problem correctly...
Still not upgraded myself to ZF2 but this is one of the way you can create a nested Select statement in ZF1 if you are using MVC architecture(try it in ZF2 also).
$table1 = new table1();
$table1->select()->from('table1',array('*',
'b' => '(SELECT x,y,z FROM table2 WHERE table2.a = table1.a)',
));
Update:
Got back to this after your comment and realized that the code I've written would not work as you will not be able select multiple columns from another table into a single column(i.e x,y,z in b).
But yes it would work in case you have to perform some agg. function on the other table which gives out a single column. e.g.
$table1 = new table1();
$table1->select()->from('table1',array('*',
'b' => '(count (*) FROM table2 WHERE table2.a = table1.a)',
));
So this would work.
This way you can get some of the columns with some function performed on them.
And the rest of the columns from the other table(table2) you can get using a join.

Local variables in an Informix script

I have to do a big update script - not an SPL (stored procedure).
It's to be written for an Informix db.
It involves inserting rows into multiple tables, each of which relies on the serial of the insert into the previous table.
I know I can get access to the serial by doing this:
SELECT DISTINCT dbinfo('sqlca.sqlerrd1') FROM systables
but I can't seem to define a local variable to store this before the insert into the next table.
I want to do this:
insert into table1 (serial, data1, data2) values (0, 'newdata1', 'newdata2');
define serial1 as int;
let serial1 = SELECT DISTINCT dbinfo('sqlca.sqlerrd1') FROM systables;
insert into table2 (serial, data1, data2) values (0, serial1, 'newdata3');
But of course Informix chokes on the define line.
Is there a way to do this without having to create this as a stored procedure, run it once and then delete the procedure?
If the number of columns in the tables involved is as few as your example, then you could make the SPL permanent, and use it to insert your data, ie:
EXECUTE PROCEDURE insert_related_tables('newdata1','newdata2','newdata3');
Obviously that doesn't scale terribly well, but is OK for your example.
Another thought that expands on Jonathan's example and solves any concurrency issues that might arise from the use of MAX() would be to include DBINFO('sessionid') in Table3:
DELETE FROM Table3 WHERE sessionid = DBINFO('sessionid');
INSERT INTO Table1 (...);
INSERT INTO Table3 (sessionid, value)
VALUES (DBINFO('sessionid'), DBINFO('sqlca.sqlerrd1'));
INSERT INTO Table2
VALUES (0, (SELECT value FROM Table3
WHERE sessionid = DBINFO('sessionid'), 'newdata3');
...
You could also make Table3 a TEMP table:
INSERT INTO Table1 (...);
SELECT DISTINCT DBINFO('sqlca.sqlerrd1') AS serial_value
FROM some_dummy_table_like_systables
INTO TEMP Table3 WITH NO LOG;
INSERT INTO Table2 (...);
Informix does not provide a mechanism outside of stored procedures for 'local variables' of the type you want. However, in the limited example you provide, this works:
CREATE TABLE Table1
(
serial SERIAL(123) NOT NULL,
data1 VARCHAR(32) NOT NULL,
data2 VARCHAR(32) NOT NULL
);
CREATE TABLE Table2
(
serial SERIAL NOT NULL,
data1 INTEGER NOT NULL,
data2 VARCHAR(32) NOT NULL
);
INSERT INTO Table1(Serial, Data1, Data2)
VALUES(0, 'newdata1', 'newdata2');
INSERT INTO Table2(Serial, Data1, Data2)
VALUES(0, DBINFO('sqlca.sqlerrd1'), 'newdata3');
SELECT * FROM Table1;
123 newdata1 newdata2
SELECT * FROM Table2;
1 123 newdata3
However, this works only because you need to insert one row into Table2. If you needed to insert more, the technique would not work well. You could, I suppose, use:
CREATE TEMP TABLE Table3
(
value INTEGER NOT NULL
);
INSERT INTO Table1(Serial, Data1, Data2)
VALUES(0, 'newdata1', 'newdata2');
INSERT INTO Table3(Value)
VALUES(DBINFO('sqlca.sqlerrd1'));
INSERT INTO Table2(Serial, Data1, Data2)
VALUES(0, (SELECT MAX(value) FROM Table3), 'newdata3');
INSERT INTO Table2(Serial, Data1, Data2)
VALUES(0, (SELECT MAX(value) FROM Table3), 'newdata4');
And so on...the temporary table for Table3 avoids problems with concurrency and MAX().

Resources