Union queryset not giving expected result, Is it a Django ORM bug? - django-queryset

I have the following models:
class PersonDetails(models.Model):
id = models.AutoField(db_column='ID', primary_key=True)
name= models.CharField(db_column='Name', max_length=20, blank=True, null=True)
class Meta:
managed = False
db_table = 'person_details'
class Npr(models.Model):
rid = models.AutoField(db_column='rid', primary_key=True)
pid = models.ForeignKey(PersonDetails, on_delete=models.CASCADE, db_column='PID')
rel = models.CharField(max_length=1)
cid = models.ForeignKey(PersonDetails, on_delete=models.CASCADE, db_column='CID')
class Meta:
managed = False
db_table = 'npr'
unique_together = (('pid', 'cid'),)
I built the following queryset:
idf = Npr.objects.select_related('cid').filter(pid = 198)
ids = Npr.objects.select_related('pid').filter(cid = 198)
all = ids.union(idf)
and the resulting SQL statement is:
(SELECT `npr`.`rid`, `npr`.`PID`, `npr`.`rel`, `npr`.`CID`, T3.`ID`, T3.`Name` FROM `npr` INNER JOIN `person_details` T3 ON (`npr`.`PID` = T3.`ID`) WHERE `npr`.`CID` = 198)
UNION
(SELECT `npr`.`rid`, `npr`.`PID`, `npr`.`rel`, `npr`.`CID`, T3.`ID`, T3.`Name` FROM `npr` INNER JOIN `person_details` T3 ON (`npr`.`CID` = T3.`ID`) WHERE `npr`.`PID` = 198)
my Npr data is:
+------+-----+-----+------+
| rid | PID | rel | CID |
+------+-----+-----+------+
| 40 | 198 | F | 2111 |
| 42 | 198 | F | 2110 |
| 1377 | 198 | F | 1243 |
| 3305 | 198 | F | 236 |
| 3379 | 198 | F | 203 |
| 3384 | 198 | H | 200 |
| 3388 | 213 | F | 198 |
| 3389 | 218 | M | 198 |
+------+-----+-----+------+
Now look at the following
>>> a0=all[0]
>>> a7=all[7]
>>>
>>> a0.cid.id
198
>>> a0.pid.id
213
a0 returns the expected results but
>>> a7.cid.id
2111
>>> a7.pid.id
2111
a7 is not returning the expected results! the expected result is a7.pid.id=198. In addition; a0.cid.id sends the following SQL statement to database:
SELECT `person_details`.`ID`, `person_details`.`Name` FROM `person_details` WHERE `person_details`.`ID` = 198 LIMIT 21
but a0.pid.id doesn't.
Why the second part of the union queryset is not giving the correct results!?
Django Version: 3.0.4
Python version: 3.7.3
Database: 10.3.22-MariaDB-0+deb10u1-log

Fortunately; I found a better way to get the same result using one queryset, that is using Q objects, the queryset will be:
all = Npr.objects.filter(Q(pid=198) | Q(cid=198))

Related

Join 3 or more tables with Comma separated values in Oracle

I've 3 tables (One parent and 2 childs) as below
Student(Parent)
StudentID | Name | Age
1 | AA | 23
2 | BB | 25
3 | CC | 27
Book(child 1)
BookID | SID | BookName | BookPrice
1 | 1 | ABC | 20
2 | 1 | XYZ | 15
3 | 3 | LMN | 34
4 | 3 | DDD | 90
Pen(child 2)
PenID | SID | PenBrandName | PenPrice
1 | 2 | LML | 20
2 | 1 | PARKER | 15
3 | 2 | CELLO | 34
4 | 3 | LML | 90
I need to join the tables and get an output as Below
StudentID | Name | Age | BookNames | TotalBookPrice | PenBrands | TotalPenPrice
1 | AA | 23 | ABC, XYZ | 35 | PARKER | 15
2 | BB | 25 | null | 00 | LML, CELLO | 54
3 | CC | 27 | LMN, DDD | 124 | LML | 90
This is the code i tried :
Select s.studentID as "StudentID", s.name as "Name", s.age as "AGE",
LISTAGG(b.bookName, ',') within group (order by b.bookID) as "BookNames",
SUM(b.bookPrice) as "TotalBookPrice",
LISTAGG(p.penBrandName, ',') within group (order by p.penID) as "PenBrands",
SUM(p.penPrice) as "TotalPenPrice"
FROM Student s
LEFT JOIN BOOK b ON b.SID = s.StudentID
LEFT JOIN PEN p ON p.SID = s.StudentID
GROUP BY s.studentID, s.name, s.age
The result i get has multiple values of Book and Pen (cross product result in multiple values)
StudentID | Name | Age | BookNames | TotalBookPrice | PenBrands | TotalPenPrice
1 | AA | 23 | ABC,ABC,XYZ,XYZ | 35 | PARKER,PARKER | 15
Please let me know how to fix this.
Instead of Joining the tables and doing aggregation, You have to aggregate first and then join your tables -
Select s.studentID as "StudentID", s.name as "Name", s.age as "AGE",
"BookNames",
"TotalBookPrice",
"PenBrands",
"TotalPenPrice"
FROM Student s
LEFT JOIN (SELECT SID, LISTAGG(b.bookName, ',') within group (order by b.bookID) as "BookNames",
SUM(b.bookPrice) as "TotalBookPrice"
FROM BOOK
GROUP BY SID) b ON b.SID = s.StudentID
LEFT JOIN (SELECT SID, LISTAGG(p.penBrandName, ',') within group (order by p.penID) as "PenBrands",
SUM(p.penPrice) as "TotalPenPrice"
FROM PEN
GROUP BY SID) p ON p.SID = s.StudentID;

Join two tables in Oracle concatenating results by id

I want to join two tables in Oracle and list the results concatenated if they have the same parent id in this way:
Table All
|id | other fields|service_id|
|------|-------------|----------|
| 827 |xxxxxxxx |null |
| 828 |xxxxxxxx |327 |
| 829 |xxxxxxxx |328 |
| 860 |xxxxxxxx |null |
| 861 |xxxxxxxx |326 |
Table Services
| id | parent_id |
| ---- | -------|
| 326 | 860 |
| 327 | 827 |
| 328 | 827 |
I want a query that returns this
|id | sub_id |
|------|---------|
| 827 | 828,829 |
| 828 | null |
| 829 | null |
| 860 | 861 |
| 861 | null |
Thanks a lot!
By joining the all table to service and then back to all in an alias you can get the ID and sub id's in a list that then just need to be combined from multiple rows into 1 column per ID.
This should get you the "Raw data" that then needs to be aggregrated...
TESTED: Rextester working example
NOTE: Since you have different "LEVELS" of depth for your sub_ID i'm not really sure what you want so 860 and 123 isn't included because it's a completely different field than the source of 827
SELECT A.ID as ID, A2.ID as Sub_ID
FROM ALL A
LEFT JOIN SERVICES S
on S.Pareint_ID = A.ID
LEFT JOIN All A2
on A2.Service_ID = S.ID
Now... if we assume that you have a version of oracle which supports ListAgg
SELECT A.ID as ID, ListAgg(A2.ID,',') within group (Order by A2.ID) as Sub_ID
FROM ALL A
LEFT JOIN SERVICES S
on S.Parent_ID = A.ID
LEFT JOIN All A2
on A2.Service_ID = S.ID
GROUP BY A.ID
Giving Us:
+----+-----+---------+
| | ID | SUB_ID |
+----+-----+---------+
| 1 | 827 | 828,829 | -- These are all.id's...
| 2 | 828 | NULL |
| 3 | 829 | NULL |
| 4 | 860 | NULL | --> Now why is 123 present as it's a service.id
| 5 | 861 | NULL |
+----+-----+---------+
**Note all is a reserved word and either needs to be escaped or if your table name really isn't all; adjust accordingly.
LISTAGG Docs

Sum value from column A + B show in C

I have data in the database for column A, B and will show the total of A+B in column C. I have a problem where I don't know how to code the sum of A+B for all example: 200 data that I have in the database using LARAVEL
+----+------------+------------+
| id | logins_sun | logins_mon |
+----+------------+------------+
| 1 | 587 | 347 |
+----+------------+------------+
| 2 | 527 | 147 |
+----+------------+------------+
| 3 | 589 | 347 |
+----+------------+------------+
| 4 | 557 | 147 |
+----+------------+------------+
| 5 | 547 | 247 |
+----+------------+------------+
Assuming you have table called logins, you can create a Model Login with following :
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Login extends Model
{
protected $table = 'logins';
}
And then do :
$records = Login::select(
'logins_sun',
'logins_mon',
\DB::raw('logins_sun + logins_mon as logins_sum')
)->get();
This will give you al rows in format:
+----+------------+------------+------------+
| id | logins_sun | logins_mon | logins_sum |
+----+------------+------------+------------+
| 1 | 587 | 347 | 934 |
+----+------------+------------+------------+
If you want to update :
\DB::table('logins)->update([
'logins_sum' => \DB::raw('logins_sun + logins_mon')
]);
DB::table('table_name')->selectRaw('logins_sun, logins_mon, (logins_sun + logins_mon) AS sum')->get();
might work. I'm kinda assuming logins_sun is column A, logins_mon is column B, and I created a new column C called sum.

Eloquent Joins and Sums

I have 2 tables and would like to get have a query which gets all columns from table 'projects' and several sums from table 'invoices' based on a field 'type'.
projects
+----+--------+------+--------+
| ID | Address| Date | Other |
+----+--------+------+--------+
| 1 | demo | date | other |
| 2 | demo2 | date2| other2 |
invoices
+----+---------+-----+--------+
| ID | proj_id | type| amount |
+--------------+-----+--------+
| 1 | 1 | a | 10 |
| 2 | 1 | a | 20 |
| 3 | 1 | b | 10 |
| 4 | 1 | b | 15 |
| 5 | 1 | c | 5 |
| 6 | 2 | a | 30 |
| 7 | 2 | a | 5 |
| 8 | 2 | b | 30 |
| 9 | 2 | c | 5 |
| 10 | 2 | c | 30 |
Using Laravel Eloquent I want to be able to get:
+----+---------+------+-------+---+---+---+
| ID | Address | Date | Other | a | b | c |
+----+---------+------+-------+---+---+---+
| 1 | demo | date | other |30 |25 | 5 |
| 2 | demo2 | date2| other2|35 |30 |35 |
Im getting stuck with the sum part, well actually the whole thing!
So far I have:
$projects = DB::table('projects')
->leftJoin('invoices', 'projects.id', '=', 'invoices.project_id')
->select('projects.*', 'invoices.*')
->get();
Which is obviously not very far along! Any help would be greatly appreciated!
You need a basic pivot query here. The easiest way to go here would probably be via a raw select:
$sql = "projects.*, ";
$sql .= "sum(case when invoices.type = 'a' then invoices.amount end) as a, ";
$sql .= "sum(case when invoices.type = 'b' then invoices.amount end) as b, ";
$sql .= "sum(case when invoices.type = 'c' then invoices.amount end) as c";
$projects = DB::table('projects')
->select(DB::raw($sql))
->leftJoin('invoices', 'projects.id', '=', 'invoices.project_id')
->groupBy('project.id')
->get();
This should correspond to the following raw MySQL query:
SELECT
p.*,
SUM(CASE WHEN i.type = 'a' THEN i.amount END) AS a,
SUM(CASE WHEN i.type = 'b' THEN i.amount END) AS b,
SUM(CASE WHEN i.type = 'c' THEN i.amount END) AS c
FROM project p
LEFT JOIN invoices I
ON p.id = i.project_id
GROUP BY
p.id;

Update Statement using Join Condition

Below is an example. TABLE 1 is manually created where the first three columns
are loaded here from an external file. Fourth column(SHOWROOM_ID) will be taken from TABLE2
and the rest of the columns in TABLE 1 will be updated based on criteria.
TABLE 1
NAME |OLD_CPR_NO |OLD_COS_NO |SHOWROOM_ID|NM_CPR_COS_MAT|NM_CPR_MAT|COS_CPR_MAT|
------------------------------------------------------------------------------------
FORD | 45 | 487 | | | |
TOYOTA | 78 | 562 | | | |
BENZ | 55 | 789 | | | |
JEEP | 66 | 124 | | | |
HONDA | 34 | 142 | | | |
KIA | 12 | 962 | | | |
GM | 89 | 7787 | | | |
CHRYSLER | 45 | 236 | | | |
AUDI | 67 | 4789 | | | |
TABLE 2
PK|NAME |OLD_CPR_NO |OLD_COS_NO |SHOWROOM_ID
---------------------------------------------
1 |FORD | 45 | 487 | 1
2 |TOYOTA | 78 | 562 | 2
3 |CIAT | 55 | 789 | 3
4 |JEEP | 66 | 124 | 5
5 |HONDA | 34 | 456 | 6
6 |MUSTANG | 12 | 962 | 7
7 |GM | 89 | 56 | 8
8 |CHRYSLER | 45 | 236 | 9
9 |AUDI | 67 | 4789 | 10
STEP 1: Update NM_CPR_COS_MAT column from table 1. This is an indicator field
where NAME,OLD_CPR_NO,OLD_COS_NO matches from TABLE 1 and TABLE 2 then assign indicator 'Y'
I was able to attain the results based on my below query:
UPDATE TABLE_1 TAB1
SET NM_CPR_COS_MAT = (SELECT 'Y'
FROM
TABLE_2 TAB2
WHERE
TRIM(TAB1.NAME) = TRIM(TAB2.NAME)
AND TRIM(TAB1.OLD_CPR_NO) = TRIM(TAB2.OLD_CPR_NO)
AND TRIM(TAB1.OLD_COS_NO) = TRIM(TAB2.OLD_COS_NO)
;
COMMIT;
UPDATE TABLE_1 TAB1
SET SHOWROOM_ID= (SELECT TAB2.SHOWROOM_ID
FROM
TABLE_2 TAB2
WHERE
TRIM(TAB1.NAME) = TRIM(TAB2.NAME)
AND TRIM(TAB1.OLD_CPR_NO) = TRIM(TAB2.OLD_CPR_NO)
AND TRIM(TAB1.OLD_COS_NO) = TRIM(TAB2.OLD_COS_NO)
AND TRIM(TAB1.NM_CPR_COS_MAT) = 'Y'
;
COMMIT;
RESULT:
TABLE 1
NAME |OLD_CPR_NO |OLD_COS_NO |SHOWROOM_ID|NM_CPR_COS_MAT|NM_CPR_MAT|COS_CPR_MAT|
------------------------------------------------------------------------------------
FORD | 45 | 487 | 1 | Y | |
TOYOTA | 78 | 562 | 2 | Y | |
BENZ | 55 | 789 | | | |
JEEP | 66 | 124 | 5 | Y | |
HONDA | 34 | 142 | | | |
KIA | 12 | 962 | | | |
GM | 89 | 7787 | | | |
CHRYSLER | 45 | 236 | 9 | Y | |
AUDI | 67 | 4789 | 10 | Y | |
But I am getting errors if I tried to use the join statements.
UPDATE TABLE_1 TAB1
SET NM_CPR_COS_MAT = 'Y'
FROM
TABLE_2 TAB2 JOIN
TABLE_1 TAB1 ON
TRIM(TAB1.OLD_CPR_NO) = TRIM(TAB2.OLD_CPR_NO)
WHERE
TRIM(TAB1.NAME) = TRIM(TAB2.NAME)
AND TRIM(TAB1.OLD_COS_NO) = TRIM(TAB2.OLD_COS_NO)
;
COMMIT;
ORA-00933: SQL command not properly ended.
From the below resulting table, I have to again UPDATE SHOWROOM_ID column and NM_CPR_MAT
TABLE 1
NAME |OLD_CPR_NO |OLD_COS_NO |SHOWROOM_ID|NM_CPR_COS_MAT|NM_CPR_MAT|COS_CPR_MAT|
------------------------------------------------------------------------------------
FORD | 45 | 487 | 1 | Y | |
TOYOTA | 78 | 562 | 2 | Y | |
BENZ | 55 | 789 | | | |
JEEP | 66 | 124 | 5 | Y | |
HONDA | 34 | 142 | | | |
KIA | 12 | 962 | | | |
GM | 89 | 7787 | | | |
CHRYSLER | 45 | 236 | 9 | Y | |
AUDI | 67 | 4789 | 10 | Y | |
STEP 2:
UPDATE TABLE_1 TAB1
SET NM_CPR_MAT = (SELECT 'Y'
FROM
TABLE_2 TAB2
WHERE
TRIM(TAB1.NAME) = TRIM(TAB2.NAME)
AND TRIM(TAB1.OLD_CPR_NO) = TRIM(TAB2.OLD_CPR_NO)
AND TRIM(NM_CPR_COS_MAT) IS NULL
;
COMMIT;
UPDATE TABLE_1 TAB1
SET SHOWROOM_ID= (SELECT TAB2.SHOWROOM_ID
FROM
TABLE_2 TAB2
WHERE
WHERE
TRIM(TAB1.NAME) = TRIM(TAB2.NAME)
AND TRIM(TAB1.OLD_CPR_NO) = TRIM(TAB2.OLD_CPR_NO)
AND TRIM(NM_CPR_COS_MAT) IS NULL
AND TRIM(NM_CPR_MAT) = 'Y'
;
COMMIT;
I AM GETTING THE BELOW RESULTS.I AM GETTING THE CORRECT 'Y' IN NM_CPR_MAT COLUMNS
AND ALSO THE CORRECT NUMBERS IN SHOWROOM_ID FOR THE NEW UPDATE STATEMENT BUT THE NUMBERS
THAT WAS UPDATED IN THE UPDATED STATEMENT WERE GONE.
TABLE 1
NAME |OLD_CPR_NO |OLD_COS_NO |SHOWROOM_ID|NM_CPR_COS_MAT|NM_CPR_MAT|COS_CPR_MAT|
------------------------------------------------------------------------------------
FORD | 45 | 487 | | Y | |
TOYOTA | 78 | 562 | | Y | |
BENZ | 55 | 789 | | | |
JEEP | 66 | 124 | | Y | |
HONDA | 34 | 142 | 6 | | Y |
KIA | 12 | 962 | | | |
GM | 89 | 7787 | 8 | | Y |
CHRYSLER | 45 | 236 | | Y | |
AUDI | 67 | 4789 | | Y | |
Incorrect syntax, missing (SELECT before 'Y', and don't forget the matching closing bracket at the end.
UPDATE TABLE_1 TAB1
SET NM_CPR_COS_MAT = (SELECT 'Y'
FROM
TABLE_2 TAB2 JOIN
TABLE_1 TAB1 ON
TRIM(TAB1.OLD_CPR_NO) = TRIM(TAB2.OLD_CPR_NO)
WHERE
TRIM(TAB1.NAME) = TRIM(TAB2.NAME)
AND TRIM(TAB1.OLD_COS_NO) = TRIM(TAB2.OLD_COS_NO));

Resources