Why does django ORM retrieve the same related object multiple times when the foreign key is the same? - django-orm

Given the following code:
class Customer(models.Model):
name = models.CharField(max_length=30)
class Project(models.Model):
name = models.CharField(max_length=30)
for p in Project.objects.all():
print (p.name, p.customer.name)
I then create the following objects:
customer 1
project 1
project 2
project 3
customer 2
project 1
project 2
project 3
The for loop prints the result is as expected:
Project 1 Customer 1
Project 2 Customer 1
Project 3 Customer 1
Project 4 Customer 2
Project 5 Customer 2
Project 6 Customer 2
By checking the connection.queries, I see that django has executed 7 queries:
'SELECT "myapp_project"."id", "myapp_project"."customer_id", "myapp_project"."name", "myapp_project"."is_active" FROM "myapp_project"'
'SELECT "myapp_customer"."id", "myapp_customer"."name", "myapp_customer"."is_active" FROM "myapp_customer" WHERE "myapp_customer"."id" = 1 LIMIT 21'
'SELECT "myapp_customer"."id", "myapp_customer"."name", "myapp_customer"."is_active" FROM "myapp_customer" WHERE "myapp_customer"."id" = 1 LIMIT 21'
'SELECT "myapp_customer"."id", "myapp_customer"."name", "myapp_customer"."is_active" FROM "myapp_customer" WHERE "myapp_customer"."id" = 1 LIMIT 21'
'SELECT "myapp_customer"."id", "myapp_customer"."name", "myapp_customer"."is_active" FROM "myapp_customer" WHERE "myapp_customer"."id" = 2 LIMIT 21'
'SELECT "myapp_customer"."id", "myapp_customer"."name", "myapp_customer"."is_active" FROM "myapp_customer" WHERE "myapp_customer"."id" = 2 LIMIT 21'
'SELECT "myapp_customer"."id", "myapp_customer"."name", "myapp_customer"."is_active" FROM "myapp_customer" WHERE "myapp_customer"."id" = 2 LIMIT 21
But my question is, why does the django ORM repeat the same query 3 times to retrieve the customer 1 associated with the projects 1, 2 and 3? I thought that the Customer data would be stored in the cache, and only 3 queries would be executed in total, like this:
'SELECT "myapp_project"."id", "myapp_project"."customer_id", "myapp_project"."name", "myapp_project"."is_active" FROM "myapp_project"'
'SELECT "myapp_customer"."id", "myapp_customer"."name", "myapp_customer"."is_active" FROM "myapp_customer" WHERE "myapp_customer"."id" = 1 LIMIT 21'
'SELECT "myapp_customer"."id", "myapp_customer"."name", "myapp_customer"."is_active" FROM "myapp_customer" WHERE "myapp_customer"."id" = 2 LIMIT 21'
I would bet that this is by design. What is the rationale behind this behavior?
Thanks
(PS: I know that could have used select_related and use only one query joining the two entities, but this is more of a conceptual question):
Project.objects.select_related('customer')

Related

Listing rows which have the same value as an entred id in a certain column in postgresql

I have a following table structure in Postgres:
id
conversation_id
member_id
3
2
73
4
2
1
5
2
2
6
3
1
8
3
73
How I can select all rows of members with the same conversation_id as the entred member_id (2) for example?
For the example above it will return rows 3, 4, 5
I tried this one query, but it does not return what I expect:
SELECT cu.id, cu.member_id
FROM conversation c, conversation_user cu
WHERE cu.conversation_id=c.id
GROUP BY cu.member_id, cu.id
having cu.member_id = 2
I found a solution:
SELECT cu.id
FROM conversation c, conversation_user cu
WHERE cu.conversation_id=c.id
AND cu.conversation_id = (SELECT cu2.conversation_id FROM conversation_user cu2 WHERE cu2.member_id=2 LIMIT 1)

Join data from 3 tables

table_1
id customer_id
---------------
1 1
2 2
3 1
4 1
5 3
6 4
table_2
id id_table1 device_mac
-------------------------------------
1 1 aa:bb:cc:dd:ee:ff
2 1 11:22:33:44:55:66
3 2 1a:2a:3a:4a:5a:6a
4 3 2b:3b:4b:5b:6b:7b
5 4 3c:4c:5c:6c:7c:8c
6 2 4d:5d:6d:7d:8d:9d
table_3
id device_mac device_name
---------------------------------------
1 aa:bb:cc:dd:ee:ff loc1
2 11:22:33:44:55:66 loc2
3 1a:2a:3a:4a:5a:6a loc3
4 2b:3b:4b:5b:6b:7b loc4
5 3c:4c:5c:6c:7c:8c loc5
6 4d:5d:6d:7d:8d:9d loc6
I have a requirement where I need to get the below details by customer_id using python and postgres db.
ex: get details with customer_id = 1
table1_id count(table_2) device_names
1 2 [loc1, loc2]
3 1 [loc4]
4 1 [loc5]
I tried with individual queries using python:
select id from table_1 where customer_id=1;
for t1_id from ids above table_1 data:
select * from table_2 where table_id=t1_id
for t2_data from ids above table2_data:
select * from table_3 where device_mac = t2_data.device_mac
# generate expected rows
Can I just do this in a signle query?
Join the tables and aggregate:
SELECT t1.id,
COUNT(*) count,
STRING_AGG(t3.device_name, ',' ORDER BY t3.device_name) device_names
FROM table_1 t1
INNER JOIN table_2 t2 ON t2.id_table1 = t1.id
INNER JOIN table_3 t3 ON t3.device_mac = t2.device_mac
WHERE t1.customer_id = 1
GROUP BY t1.id
If you are getting duplicate device_names you may use DISTINCT:
STRING_AGG(DISTINCT t3.device_name, ',' ORDER BY t3.device_name) device_names
See the demo.
Results:
id
count
device_names
1
2
loc1,loc2
3
1
loc4
4
1
loc5

TSQL - Count on a date

is it possible to make a statistic with the queries starting from the data so configured?
Table a: registry
id (key)
name
able b: holidays
id (key)
id_anagrafica (foreign key)
data_start
data_end
Query:
SELECT b.id, a.name, b.start_date, b.end_date
FROM registry to INNER JOIN
      holidays b ON (a.id = b.id_anagrafica)
WHERE b.start_date> = getdate ()
So doing I get:
id, name, start_date, end_date
1, Mario, 01/06/2018, 30/06/2018
2, Marino, 08/06/2018, 25/06/2018
3, Maria, 01/07/2018, 05/07/2018
-
-
-
Having only a start_date and end_date I can not know in a day how many people are on holidays.
What I need is:
data, num_pers_in_ferie
01/06/2018, 1
06/02/2018, 1
03/06/2018, 1
-
-
08/06/2018, 2
Can you help me?
Thanks in advance
Check the approach below
create table #registry (id int, name nvarchar(50))
insert into #registry values
(1, 'Mario'),
(2, 'Marino'),
(3, 'Maria')
create table #holidays (id int,id_anagrafica int,data_start date,data_end date)
insert into #holidays
select id, id, '2018-06-01', '2018-06-30'
from #registry
update #holidays set data_start = dateadd(day, 20, data_start), data_end = dateadd(day, -5, data_end)
where id = 2
update #holidays set data_start = dateadd(day, 14, data_start)--, data_end = dateadd(day, -10, data_end)
where id = 3
SELECT b.id, a.name, b.data_start, b.data_end
FROM #registry a
INNER JOIN
#holidays b ON (a.id = b.id_anagrafica)
WHERE b.data_start > = getdate ()
DECLARE #startDate DATETIME=CAST(MONTH(GETDATE()) AS VARCHAR) + '/' + '01/' + + CAST(YEAR(GETDATE()) AS VARCHAR) -- mm/dd/yyyy
DECLARE #endDate DATETIME= GETDATE() -- mm/dd/yyyy
select [DATA] = convert(date, DATEADD(Day,Number,#startDate)),
--se ti serve in italiano usa la riga sotto
--[DATA] = CONVERT(varchar, DATEADD(Day,Number,#startDate), 103)
SUM(case when DATEADD(Day,Number,#startDate) between data_start and data_end then 1 else 0 end) Pers_in_Ferie
from master..spt_values c,
#registry a
INNER JOIN
#holidays b ON (a.id = b.id_anagrafica)
where c.Type='P' and DATEADD(Day,Number,#startDate) >=data_start and DATEADD(Day,Number,#startDate) <=data_end
group by DATEADD(Day,Number,#startDate)
order by [DATA]
drop table #holidays
drop table #registry
Output:
DATA Pers_in_Ferie
---------- -------------
2018-06-01 1
2018-06-02 1
2018-06-03 1
2018-06-04 1
2018-06-05 1
2018-06-06 1
2018-06-07 1
2018-06-08 1
2018-06-09 1
2018-06-10 1
2018-06-11 1
2018-06-12 1
2018-06-13 1
2018-06-14 1
2018-06-15 2
2018-06-16 2
2018-06-17 2
2018-06-18 2
2018-06-19 2
2018-06-20 2
2018-06-21 3
2018-06-22 3
2018-06-23 3
2018-06-24 3
2018-06-25 3
2018-06-26 2
2018-06-27 2
2018-06-28 2
2018-06-29 2
2018-06-30 2
(30 rows affected)

Column name determined by parameter

I've tried both approaches. First one has a syntax error. Second one shoves every column in the result instead of just the one that has a match with #LabelID.
SELECT (SELECT CASE #LabelID
WHEN 1 THEN count(h.ee_cmn_idfr) as 'DIR'
WHEN 2 THEN count(h.ee_cmn_idfr) as 'DD'
WHEN 3 THEN count(h.ee_cmn_idfr) as 'OD_Staff'
WHEN 4 THEN count(h.ee_cmn_idfr) as 'DI_BC'
WHEN 5 THEN count(h.ee_cmn_idfr) as 'DI_PLs'
WHEN 6 THEN count(h.ee_cmn_idfr) as 'DI_PQAs'
WHEN 7 THEN count(h.ee_cmn_idfr) as 'DI_FTEs'
WHEN 8 THEN count(h.ee_cmn_idfr) as 'AIPQBBC'
WHEN 9 THEN count(h.ee_cmn_idfr) as 'AIPQB_PL'
WHEN 10 THEN count(h.ee_cmn_idfr) as 'AIPQB_PQA'
WHEN 11 THEN count(h.ee_cmn_idfr) as 'AIPQB_GS13S'
WHEN 12 THEN count(h.ee_cmn_idfr) as 'AIPQB_FTE'
WHEN 13 THEN count(h.ee_cmn_idfr) as 'IT_Staff'
WHEN 14 THEN count(h.ee_cmn_idfr) as 'IT_Sup'
)
second approach:
SELECT
CASE WHEN #LabelID = 1 THEN count(h.ee_cmn_idfr) as 'DIR'
,CASE WHEN #LabelID = 2 THEN count(h.ee_cmn_idfr) END as 'DD'
,CASE WHEN #LabelID = 3 THEN count(h.ee_cmn_idfr) END as 'OD_Staff'
,CASE WHEN #LabelID = 4 THEN count(h.ee_cmn_idfr) END as 'DI_BC'
,CASE WHEN #LabelID = 5 THEN count(h.ee_cmn_idfr) END as 'DI_PLs'
,CASE WHEN #LabelID = 6 THEN count(h.ee_cmn_idfr) END as 'DI_PQAs'
,CASE WHEN #LabelID = 7 THEN count(h.ee_cmn_idfr) END as 'DI_FTEs'
,CASE WHEN #LabelID = 8 THEN count(h.ee_cmn_idfr) END as 'AIPQBBC'
,CASE WHEN #LabelID = 9 THEN count(h.ee_cmn_idfr) END as 'AIPQB_PL'
,CASE WHEN #LabelID = 10 THEN count(h.ee_cmn_idfr) END as 'AIPQB_PQA'
,CASE WHEN #LabelID = 11 THEN count(h.ee_cmn_idfr) END as 'AIPQB_GS13S'
,CASE WHEN #LabelID = 12 THEN count(h.ee_cmn_idfr) END as 'AIPQB_FTE'
,CASE WHEN #LabelID = 13 THEN count(h.ee_cmn_idfr) END as 'IT_Staff'
,CASE WHEN #LabelID = 14 THEN count(h.ee_cmn_idfr) END as 'IT_Sup'
Here is how you would do it using dynamic SQL:
SAMPLE DATA:
IF OBJECT_ID('tempdb..#INPUT') IS NOT NULL
DROP TABLE #INPUT;
CREATE TABLE #INPUT(RowID INT IDENTITY(1, 1)
, ee_cmn_idfr INT);
INSERT INTO #INPUT(ee_cmn_idfr)
VALUES
(1),
(1),
(1),
(1),
(1),
(1),
(1),
(1),
(1),
(1),
(1),
(1);
SQL QUERY:
DECLARE #LabelID INT = 1; --<-- set the labelID
DECLARE #SQL NVARCHAR(MAX) = ''; --<-- declare a variable to hold the dynamic sql
SELECT #SQL = 'SELECT COUNT(ee_cmn_idfr) AS '+QUOTENAME(CASE #LabelID
WHEN 1 THEN 'DIR'
WHEN 2 THEN 'DD'
WHEN 3 THEN 'OD_Staff'
WHEN 4 THEN 'DI_BC'
WHEN 5 THEN 'DI_PLs'
WHEN 6 THEN 'DI_PQAs'
WHEN 7 THEN 'DI_FTEs'
WHEN 8 THEN 'AIPQBBC'
WHEN 9 THEN 'AIPQB_PL'
WHEN 10 THEN 'AIPQB_PQA'
WHEN 11 THEN 'AIPQB_GS13S'
WHEN 12 THEN 'AIPQB_FTE'
WHEN 13 THEN 'IT_Staff'
WHEN 14 THEN 'IT_Sup'
END)+' FROM #INPUT'; --<-- you'll have to change the name of the table accordingly
PRINT(#SQL); --<-- print out the query not needed but nice to have for debuging
EXEC (#SQL); --<-- execute the dynamic sql
the PRINT(#SQL) will print the following:
SELECT COUNT(ee_cmn_idfr) AS [DIR] FROM #INPUT
RESULTS:
the above code will select the column name as follows:
WHEN 1 THEN 'DIR'
WHEN 2 THEN 'DD'
WHEN 3 THEN 'OD_Staff'
WHEN 4 THEN 'DI_BC'
WHEN 5 THEN 'DI_PLs'
WHEN 6 THEN 'DI_PQAs'
WHEN 7 THEN 'DI_FTEs'
WHEN 8 THEN 'AIPQBBC'
WHEN 9 THEN 'AIPQB_PL'
WHEN 10 THEN 'AIPQB_PQA'
WHEN 11 THEN 'AIPQB_GS13S'
WHEN 12 THEN 'AIPQB_FTE'
WHEN 13 THEN 'IT_Staff'
WHEN 14 THEN 'IT_Sup'

Making a query in MS Access

I am having a problem with my query. I have 2 tables:
Table 1 is AutoCompany it has fields company and CodeCar. CodeCar can be 3 or 4 depending on the type of car that company has.
table 1: AutoCompany
company| CodeCar|
jora 3
jora 4
jora 3
ghita 3
ghita 3
ghita 4
gheorghe 4
gheorghe 3
gheorghe 3
Table 2 CodeCarCompanies has the codes:
car | codeCar
mers 3
vW 4
I need to select the companies with the count of the occurance of the 2 codeCars
resulting in something like this:
company | MERS| VW
jora 2 1
ghita 2 1
gheorghe 2 1
My attempt so far:
SELECT COUNT(dbo.AutoComany) AS MERS, dbo.Company, COUNT(dbo.AutoComany.
[CodeCar]) AS VW,
FROM dbo.AutoComany FULL OUTER JOIN
dbo.AutoComany ON dbo.АВТОМОБ.КодПредпр = AutoCompany.company
WHERE (dbo.CodeCarComapnies.[CodeCar] = 3)
GROUP BY dbo..company, dbo.CodeCarComapnies.[CodeCar]
HAVING (dbo.CodeCarComapnies.[CodeCar] = 4)
In MS Access, I think you want:
SELECT codecarcomapnies.car,
Count(autocompany.codecar) AS CountOfCodeCar
FROM autocompany
INNER JOIN codecarcomapnies
ON autocompany.codecar = codecarcomapnies.codecar
WHERE autocompany.codecar IN ( 3, 4 )
GROUP BY codecarcomapnies.car;
The above was built using the MS Access query design window and the Sum Σ button
Edit re Comment
SELECT Sum(IIf([autocompany].[codecar]=3,1,0)) AS mers,
Sum(IIf([autocompany].[codecar]=4,1,0)) AS vw
FROM autocompany
Or
TRANSFORM Count(autocompany.CodeCar) AS CountOfCodeCar
SELECT "Total" AS Total
FROM autocompany
INNER JOIN CodeCarComapnies
ON autocompany.CodeCar = CodeCarComapnies.codeCar
WHERE autocompany.CodeCar In (3,4)
GROUP BY "Total"
PIVOT CodeCarComapnies.car