SELECT instead of TABLE and WHERE in UPDATE? - sql

I've seen may answers to the same kind of question but I still doubt.
UPDATE in SQL should be something like :
UPDATE *Table*
SET *choose value*
WHERE *what do we change*
I would like to know if there is possibilites to use a select instead of TABLE (an so instead of WHERE)
Like
UPDATE *Select conditions and rows*
SET *What do we change (the where is implicit)
I know UPDATE/SET/WHERE works well, but I'm exploring other possibilites :)
Thanks,
Nicolas
EXAMPLE :
Have to do :
update produits
set `NO_FOURNISSEUR` = "30"
where `NO_FOURNISSEUR` = "3"
would like to try something like :
update select * from produits where produits.`no_fournisseur`= "30"
set `NO_FOURNISSEUR`= "3"

MariaDB has a with expression, like so:
https://mariadb.com/kb/en/library/with/
so you would have:
WITH t AS (
select *
from produits
where produits.no_fournisseur= '30')
UPDATE t SET t.no_fournisseur = '3';

Yes. The ANSI SQL standard way to do this is using a Common Table Expression:
with dt as
(
select *
from produits
where produits.no_fournisseur= '30'
)
update dt set NO_FOURNISSEUR = '3'
This standard syntax supports joins and other query constructs in the SELECT part, and gives you a simple way to examine the rows before applying the update.

Related

how to get a value from the results of another column calculation in same table

I tried to retrieve a value from the calculation of several columns,
in this case try to apply the formula "(a + (a / 25 * b)) - c" to be processed using sql language which I will use in codeigniter.
I also tried using "derived table" like SELECT .... FROM (SELECT... FROM...) AS dt
but I had difficulty when applying it to my case in codeigniter
private function _get_datatables_query(){
$intvl = '2';
$tgl_stok = '2019-09-30';
$this->db->SELECT('p.hso, p.no_part, p.nama_part, jml, sp.oh, sum(p.qty)+(sum(p.qty)/25*$intvl)-sp.oh as suggest');
$this->db->FROM('penjualan p');
$this->db->JOIN('stok_part sp', 'sp.no_part = p.no_part', 'left');
$this->db->WHERE("sp.tgl = '$tgl_stok' AND p.tgl BETWEEN DATE_SUB('$tgl_stok', INTERVAL $intvl DAY) AND '$tgl_stok'");
$this->db->GROUP_BY('p.no_part');
//...other code...
}
I want a column with the alias suggest in the code to produce a calculated value of several other columns
I know writing code that I created is not in accordance with the rules of writing SQL, I tried a number of ways but it did not work. I am very grateful for your help
Instead of var you should use parameter in this way you can easly pass the value you need and avoid sqlinjection eg:
$sql = "SELECT p.hso, p.no_part, p.nama_part, jml, sp.oh, sum(p.qty)+(sum(p.qty)/25*?)-sp.oh as suggest
FROM enjualan p
LEFT JOIN stok_part sp ON sp.no_part = p.no_part
WHERE sp.tgl = ? AND p.tgl BETWEEN DATE_SUB(?, INTERVAL ? DAY) AND ?
GROUP BY p.no_part";
$this->db->query($sql, array($intval,$tgl_stok, $tgl_stok, $intvl, tgl_stok ));
}
I was solved this case.
the problem is in my query:
$this->db->SELECT('p.hso, p.no_part, p.nama_part, jml, sp.oh, sum(p.qty)+(sum(p.qty)/25*$intvl)-sp.oh as suggest');
and replace it with
$this->db->SELECT("p.hso, p.no_part, p.nama_part, sum(p.qty) as jml, sp.oh, sum(p.qty)+(sum(p.qty)/25*'$intvl')-sp.oh as s_po");
my mistake was writing quotation marks in the query

How to select from subquery using Laravel Query Builder?

I'd like to get value by the following SQL using Eloquent ORM.
- SQL
SELECT COUNT(*) FROM
(SELECT * FROM abc GROUP BY col1) AS a;
Then I considered the following.
- Code
$sql = Abc::from('abc AS a')->groupBy('col1')->toSql();
$num = Abc::from(\DB::raw($sql))->count();
print $num;
I'm looking for a better solution.
Please tell me simplest solution.
In addition to #delmadord's answer and your comments:
Currently there is no method to create subquery in FROM clause, so you need to manually use raw statement, then, if necessary, you will merge all the bindings:
$sub = Abc::where(..)->groupBy(..); // Eloquent Builder instance
$count = DB::table( DB::raw("({$sub->toSql()}) as sub") )
->mergeBindings($sub->getQuery()) // you need to get underlying Query Builder
->count();
Mind that you need to merge bindings in correct order. If you have other bound clauses, you must put them after mergeBindings:
$count = DB::table( DB::raw("({$sub->toSql()}) as sub") )
// ->where(..) wrong
->mergeBindings($sub->getQuery()) // you need to get underlying Query Builder
// ->where(..) correct
->count();
Laravel v5.6.12 (2018-03-14) added fromSub() and fromRaw() methods to query builder (#23476).
The accepted answer is correct but can be simplified into:
DB::query()->fromSub(function ($query) {
$query->from('abc')->groupBy('col1');
}, 'a')->count();
The above snippet produces the following SQL:
select count(*) as aggregate from (select * from `abc` group by `col1`) as `a`
The solution of #JarekTkaczyk it is exactly what I was looking for. The only thing I miss is how to do it when you are using
DB::table() queries. In this case, this is how I do it:
$other = DB::table( DB::raw("({$sub->toSql()}) as sub") )->select(
'something',
DB::raw('sum( qty ) as qty'),
'foo',
'bar'
);
$other->mergeBindings( $sub );
$other->groupBy('something');
$other->groupBy('foo');
$other->groupBy('bar');
print $other->toSql();
$other->get();
Special atention how to make the mergeBindings without using the getQuery() method
From laravel 5.5 there is a dedicated method for subqueries and you can use it like this:
Abc::selectSub(function($q) {
$q->select('*')->groupBy('col1');
}, 'a')->count('a.*');
or
Abc::selectSub(Abc::select('*')->groupBy('col1'), 'a')->count('a.*');
There are many readable ways to do these kinds of queries at the moment (Laravel 8).
// option 1: DB::table(Closure, alias) for subquery
$count = DB::table(function ($sub) {
$sub->from('abc')
->groupBy('col1');
}, 'a')
->count();
// option 2: DB::table(Builder, alias) for subquery
$sub = DB::table('abc')->groupBy('col1');
$count = DB::table($sub, 'a')->count();
// option 3: DB::query()->from(Closure, alias)
$count = DB::query()
->from(function ($sub) {
$sub->from('abc')
->groupBy('col1')
}, 'a')
->count();
// option 4: DB::query()->from(Builder, alias)
$sub = DB::table('abc')->groupBy('col1');
$count = DB::query()->from($sub, 'a')->count();
For such small subqueries, you could even try fitting them in a single line with PHP 7.4's short closures but this approach can be harder to mantain.
$count = DB::table(fn($sub) => $sub->from('abc')->groupBy('col1'), 'a')->count();
Note that I'm using count() instead of explicitly writing the count(*) statement and using get() or first() for the results (which you can easily do by replacing count() with selectRaw(count(*))->first()).
The reason for this is simple: It returns the number instead of an object with an awkwardly named property (count(*) unless you used an alias in the query)
Which looks better?
// using count() in the builder
echo $count;
// using selectRaw('count(*)')->first() in the builder
echo $count->{'count(*)'};
Correct way described in this answer: https://stackoverflow.com/a/52772444/2519714
Most popular answer at current moment is not totally correct.
This way https://stackoverflow.com/a/24838367/2519714 is not correct in some cases like: sub select has where bindings, then joining table to sub select, then other wheres added to all query. For example query:
select * from (select * from t1 where col1 = ?) join t2 on col1 = col2 and col3 = ? where t2.col4 = ?
To make this query you will write code like:
$subQuery = DB::query()->from('t1')->where('t1.col1', 'val1');
$query = DB::query()->from(DB::raw('('. $subQuery->toSql() . ') AS subquery'))
->mergeBindings($subQuery->getBindings());
$query->join('t2', function(JoinClause $join) {
$join->on('subquery.col1', 't2.col2');
$join->where('t2.col3', 'val3');
})->where('t2.col4', 'val4');
During executing this query, his method $query->getBindings() will return bindings in incorrect order like ['val3', 'val1', 'val4'] in this case instead correct ['val1', 'val3', 'val4'] for raw sql described above.
One more time correct way to do this:
$subQuery = DB::query()->from('t1')->where('t1.col1', 'val1');
$query = DB::query()->fromSub($subQuery, 'subquery');
$query->join('t2', function(JoinClause $join) {
$join->on('subquery.col1', 't2.col2');
$join->where('t2.col3', 'val3');
})->where('t2.col4', 'val4');
Also bindings will be automatically and correctly merged to new query.
I like doing something like this:
Message::select('*')
->from(DB::raw("( SELECT * FROM `messages`
WHERE `to_id` = ".Auth::id()." AND `isseen` = 0
GROUP BY `from_id` asc) as `sub`"))
->count();
It's not very elegant, but it's simple.
This works fine
$q1 = DB::table('tableA')->groupBy('col');
$data = DB::table(DB::raw("({$q1->toSql()}) as sub"))->mergeBindings($q1)->get();
I could not made your code to do the desired query, the AS is an alias only for the table abc, not for the derived table.
Laravel Query Builder does not implicitly support derived table aliases, DB::raw is most likely needed for this.
The most straight solution I could came up with is almost identical to yours, however produces the query as you asked for:
$sql = Abc::groupBy('col1')->toSql();
$count = DB::table(DB::raw("($sql) AS a"))->count();
The produced query is
select count(*) as aggregate from (select * from `abc` group by `col1`) AS a;
->selectRaw('your subquery as somefield')
Deriving off mpskovvang's answer, here is what it would look like using eloquent model. (I tried updating mpskovvang answer to include this, but there's too many edit requests for it.)
$qry = Abc::where('col2', 'value')->groupBy('col1')->selectRaw('1');
$num = Abc::from($qry, 'q1')->count();
print $num;
Produces...
SELECT COUNT(*) as aggregate FROM (SELECT 1 FROM Abc WHERE col2='value' GROUP BY col1) as q1

sql select with one value of two where

This is the only place that I get all answer ;)
I want to select :
SELECT
RTRIM(LTRIM(il.Num_bloc)) AS Bloc,
RTRIM(LTRIM(il.num_colis)) AS Colis,
cd.transporteur AS Coursier,
cd.origine AS Origine,
cd.destination AS Destinataire,
cd.adresse AS [Adresse Destinataire],
cd.poids AS Poids,
il.Signataire, il.num_cin AS CIN, il.date_livraison AS [Date Livraison]
FROM
dbo.cd
INNER JOIN
dbo.il ON cd.bloc = il.Num_bloc AND dbo.cd.colis = dbo.il.num_colis
WHERE
(il.Num_bloc = RTRIM(LTRIM(#ParamBloc)))
AND (il.num_colis = RTRIM(LTRIM(#ParamColis)))
In the way of getting result if the user put ether #ParamBloc or #ParamColis
Try using IsNull() function.
A simple query would go like this
Select * from yourTable
Where
Num_bloc = ISNULL(#ParamBloc, Num_block) AND
num_colis = ISNULL(#ParamColis, num_colis)
The second parameter would make the expression to true if the #parameter Bloc or Colis is null. This query would be useful for all 4 possible combination of these two parameter.

Selects in Joins

I have query
UPDATE THD
SET RepostFlag = 'Y'
,RunListNoRetroPolicyPrepay = ?
,RetroObject = ?
FROM TranHead AS THD
JOIN (
SELECT CustPolicyNo AS CustPolicyNo
,MIN(PremPeriod) AS PremPeriod
FROM TranHead
WHERE RepostFlag = 'Y'
AND PayoutTypeNo = ?
GROUP BY CustPolicyNo
) AS THDToBeReposted ON THD.CustPolicyNo = THDToBeReposted.CustPolicyNo
WHERE THD.RepostFlag = 'N'
AND THD.PremPeriod > THDToBeReposted.PremPeriod
fails in H2 with following message
Table "THD" not found;
I looked at http://www.h2database.com/html/grammar.html#table_expression to see if H2 supports selects in join. It appears it does. Maybe I am missing something when looking at the grammar, but it seems to me that the query should work in H2.
Anyone see what is wrong?
Thanks.
I don't believe FROM is allowed in the UPDATE syntax.
You can't update an alias, you need to have the table name specified.
Complementary to other answers, JOIN (just as FROM) is not allowed in UPDATE for H2. It would be allowed in a sub query.
Essentially, stick to the basic syntax:
UPDATE SomeTable as SomeAlias
SET SomeField = ?
WHERE (%GoWild%)
Whether or not you need the alias is up to your where clause.
Reference: http://www.h2database.com/html/grammar.html#update

No Result Returned From SQL Query

I am running the following query, but no rows are returned even though a record exists that should match the query.
SELECT
*
FROM
tblsignup
WHERE
usr_email='amir#gmail.com'
AND
(status=1 or status=2)
You should try by simplifying the query (yeah...even if it's so simple)
try this
Select * from tblsignup
then
Select * from tblsignup where
usr_email = 'amir#gmail.com'
then
Select * from tblsignup where
usr_email='amir#gmail.com' and
status > 0
//I know you won't use > 0 at the end, but we want to eliminate the most cause of error we simplify by > 0 only to be easier to read
Tell us from where you start getting 0 line, this could lead us to the problem, I know I already had a problem like that with a field named "date", because date is already used by MySQL, funny MySQL still let me use that fieldname tho.
Try this:
select * from `tblsignup` where `usr_email`='amir#gmail.com' and (`status`=1 or `status`=2)
I have a feeling "status" might be reserved for something special. It might be worth a shot changing it to `status`.
Try wrapping brackets around the status column name:
SELECT *
FROM tblsignup
WHERE usr_email = 'amir#gmail.com'
AND ([status] = 1
OR [status] = 2);
EDIT
After reading your comment, why not use:
SELECT *
FROM tblsignup
WHERE usr_email = 'amir#gmail.com'
AND [status] > 0;
May it be that your column or table has case sensitive collation and the address is typed different ('Amir...')? As your query is correct SQL. You can find that with:
EXEC sp_help DatabaseName