0

I'm noob when it's come to DBMS so i wrote a query on couple of relations and i want to know what's the best way to do it because i don't feel good about my query.

Here are the relations:

CREATE TABLE "Product" (
  "id" SERIAL PRIMARY KEY NOT NULL,
  "name" text,
  "unit_price" float8,
  "quantity" integer,
  "produce_per_day_ratio" integer,
  "sold_till_now" integer
);

CREATE TABLE "Order" (
  "id" SERIAL PRIMARY KEY NOT NULL,
  "personal_id" bigint,
  "product_id" bigint,
  "quantity" bigint,
  "company" text
);

CREATE TABLE "SalesManager" (
  "id" SERIAL PRIMARY KEY NOT NULL,
  "fName" text,
  "lName" text,
  "age" integer,
  "gender" bigint,
  "salary" bigint,
  "profit" float8
);

CREATE TABLE "Gender" (
  "id" SERIAL PRIMARY KEY NOT NULL,
  "name" text
);

ALTER TABLE "Order" ADD FOREIGN KEY ("personal_id") REFERENCES "SalesManager" ("id");

ALTER TABLE "Order" ADD FOREIGN KEY ("product_id") REFERENCES "Product" ("id");

ALTER TABLE "SalesManager" ADD FOREIGN KEY ("gender") REFERENCES "Gender" ("id");

So the question is "show the name of people who have sold the most products?" and by most sold i meant (quantity * unit_price)

EDIT:this is the updated query:

WITH orderProduct AS(
  SELECT 
    O.personal_id as pId , 
    sum(O.quantity * P.unit_price) as sold_amount,
    RANK() OVER (ORDER BY sum(O.quantity * P.unit_price) DESC) as rank
  FROM "Order" AS O
  INNER JOIN "Product" AS P ON O.product_id = P.id
  GROUP BY O.personal_id
)
SELECT concat("fName" , ' ' , "lName") as name , orderProduct.sold_amount
FROM "SalesManager"
INNER JOIN orderProduct ON id = pId
WHERE rank = 1;

So how bad is it ? :(

PS: i'm using postgres 12.2

7
  • 2
    Unrelated to your problem, but: you should really avoid those dreaded quoted identifiers. They are much more trouble than they are worth it. wiki.postgresql.org/wiki/…
    – user1822
    Commented May 13, 2020 at 13:41
  • You don't really need the derived table (OP) - you can apply the rank() directly inside the CTE. But apart from that (and the dreaded quoted identifiers) this doesn't look too bad to me.
    – user1822
    Commented May 13, 2020 at 13:43
  • the column i want i used in rank is sold_amount and that is sum(O."quantity" * P."unit_price") and i tried to use it like this SELECT O."personal_id" as "pId" , sum(O."quantity" * P."unit_price") as "sold_amount", RANK() OVER (ORDER BY "sold_amount" DESC) as "rank" FROM "Order" AS O INNER JOIN "Product" AS P ON O."product_id" = P.id GROUP BY O."personal_id" but this throws an error saying sold_amount does not exists. Thank you.
    – itsloop
    Commented May 13, 2020 at 14:11
  • So i tried another thing and that's replacing sold_amount with sum() so its like this now RANK() OVER (ORDER BY sum(O."quantity" * P."unit_price") DESC) as "rank" isn't this calculating the sum twice and isn't that bad ?
    – itsloop
    Commented May 13, 2020 at 14:22
  • @a_horse_with_no_name as you said i changed my query and updated it in the question. however i have another question. Since i'm using sum both in column list and in rank isn't that an extra calculation? and is it ok to do that ?
    – itsloop
    Commented May 13, 2020 at 14:51

1 Answer 1

0

One thing that can be improved is the choice of data types:

  • for fields like company or product name varchar(50) (or a bit more) is sufficient. Text is overkill. You are not storing big chunks of text like forum posts.
  • for age the realistic range would be 18-70. So smallint is more than enough (range: -32768 to +32767)
  • usage of bigint is not justified in your schema - consider the range, it's totally overblown for your purpose:

bigint 8 bytes large-range integer -9223372036854775808 to +9223372036854775807

(source: PostgreSQL 9.5.21 Documentation - Chapter 8. Data Types)

  • for gender: an enumerated type would be a good candidate because you are really expecting no more than a few options: Male/Female/Other.
  • for unit_price you chose: float8, if I'm correct the usage range is: 1E-307 to 1E+308. That may be useful if you are trying to count the number of atoms in the universe but no item on Earth can be priced that much. It would be more reasonable to use NUMERIC and choose the precision you want, depending on how many digits you want to the right of the dot. For example retail items may need 2 decimals if you are pricing in cents, when buying in bulk from suppliers prices may be expressed with 4 decimals.
  • ditto for salary

Here you are doing arithmetic operations like SUM so the performance is impacted by the data type although you may not notice it. On bigger datasets the impact could be more noticeable.

So I think you should review the data types available on that particular DBMS and redesign your tables with an effort to select the most appropriate data type for each field.

You are the designer and maybe the end user too, so you should know more or less what kinds of values are expected and the data types should be chosen accordingly, with some room for growth if you want but stay reasonable.

Since your question is about optimizing, another point that has not been addressed but that is strongly related to performance is indexing. If you join tables that have lots of records you will need indexes sooner or later, or retrieval times will become unacceptably slow. Good table design is important too.

1
  • Thank you for your complete answer. about the data types i have to say that this noting more that a question so i wanted to make something quick and focus more on the query that i wrote. BTW "count the number of atoms in the universe" nice one. And again thank you for the complete answer.
    – itsloop
    Commented May 13, 2020 at 17:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.