Sorting Twice with an SQL Query - sql

So, say I have a table of entries which have a product name, a user, and the product's pricing.
My problem is that I want to obtain a result set that groups the products bought a single user together, and then sorts those products lexicographically.
So, something like where every product bought a user whose name starts with an A is grouped in their own little block, with each product also appearing in alphabetical order (Candy before Cat food, for example), with a user whose name starts with P afterward.
Can someone explain how I might begin to do this?

An SQL query returns a table of rows and columns. You can have one column for the client and another for the product and sort by client and inside by product (ORDER BY client, product). You don't get different "blocks" of data.
If you want this more beautiful, you need some software to create a report (i.e. data with a layout) based on the query.
What you can do with SQL, though, is suppress data, such as:
select
case when client = lag(client) over (order by client, product) then null else client end
as client,
product
from bought
order by client, product;
Sample result:
client | product
--------+--------
Max | cup
| saucer
| plate
Elsa | mug
| plate

Related

How do I find out repeat customers at a per product level, postgreSQL

I'm a real novice with SQL, but despite half a day of googling and tutorials I can't quite crack my specific use case.
I would like to know how often customers buy the same product, I have a table with products, containing both a friendly name and corresponding IDs, and another table showing each purchase record that contains the customer ID and the Product ID. Where it gets tricky is - the product name can be the same, but have a different ID depending on the date, eg. "Soap" will appear 4 times, but with 4 different IDs for a period of time so a customer has bought 4 soaps, 2 of one ID and 1 each of another.
My ideal output is something like:
customerid
productname
Repeats
ID1
soap
5
ID2
soap
2
ID3
soap
3
where those customers may have bought soaps from 3 different batches, but all up they have 3 soaps - and that's the number I care about. Been struggling with how to pull this together so any help is super appreciated.
You can do the following for use case:
select customerid, productname, count(*) as repeats
from yourtable
group by customerid, productname
order by customerid;

Exchanging rows and columns in

I am looking for a way to somehow bring the information of different rows in to different/one column.
This is the problem:
Assume that I have 10 different sellers and 1000 buyers.
Currently this is how data structured (picture)
So for each sale, I have a row with the id of the seller and a buyer who bought something from him. What I want to have is to have 1 row for each seller and then in one/different columns I want to see the id of the buyer.
I am using snowflake and read about different ways but none of them really works.
I also have a timestamp column in this table and the only thing that I could think up until now is to
SELECT seller,
rank()over(partition by seller order by purchase_date desc)
and after that I can use aggregation functions - this is not really a wise solution and not really practical when I have 10 -20 or more buyers.
What is the best approach for solving these types of problems?
You should be able to do this using listagg function as per the snowflake documentation
https://docs.snowflake.com/en/sql-reference/functions/listagg.html
--This will create a concatenated list of buyers seperated by a comma
select seller
,listagg(buyer,',') within group(order by buyer)
from table
group by seller

Applying calculations based on a number of criteria stored in separate tables?

What I need to create is a table containing "Rules" such as overriding prices and applying percentage increases to the price of stock.
For Example:
Sales Price is select from the table containing information about products, then the system needs to check another table to see if that Customer/Product/Product Category has any price rules set against it, such as percentage discount or set price to be overridden to.
How do I get access to first of all check if the customer in question exists in the table, then if the product exists and then if the category exists; and then apply the price change that is stored?
So far we have a PriceRules table that contains the headers:
RuleID | CustomerID | Product Code | Category | Price | Percentage | DateApplied | AppliedBy
The plan is to store the different variables in each column and then search based on the columns.
I'm sure this sounds really confusing so I will be around to answer queries as quickly as possible.
Thanks in advance,
Bob P
You can get these results using SQL JOINs:
SELECT ...
Product.ProductPrice as Price,
CustomerRules.ProductPriceRules as Rules
FROM Product
LEFT JOIN Customer
ON ...
LEFT JOIN CustomerRules
ON Product.ProductID = CustomerRules.ProductID
AND Customer.CustomerID = CustomerRules.CustomerID
LEFT JOIN will return ONLY matching results if any exist, if record does not exist all CustomerRules fields will contain NULL values

SQL Server: How do I maintain data integrity using aggregate functions with group by?

Here's my question: how do I maintain record integrity using aggregate functions with a group by?
To explain further, here's an example.
I have a table with the following columns: (Think of it as an "order" table)
Customer_Summary (first 10 char of name + first 10 char of address)
Customer_Name
Customer_Address
Customer_Postal Code
Order_weekday
There is one row per "order", so many rows with the same customer name, address, and summary.
What I want to do is show the customer's name, address, and postal code, as well as the number of orders they've placed on each weekday, grouped by the customer's summary.
So the data should look like:
Summary | Name | Address | PCode | Monday | Tuesday | Wednesday | Thursday | Friday
test custntest addre|test custname|test address|123456 | 1 | 1 | 1 | 1 | 1
I only want to group records of similar customer summary together, but obviously I want one name, address, and postal code to show. I'm using min() at the moment, so my query looks like:
SELECT Customer_Summary, min(customer_name), min(customer_address), min(customer_postal_code)
FROM Order
Group by customer_summary
I've omitted my weekday logic as I didn't think it was necessary.
My issue is this - some of these customers with the same customer summary have different addresses and postal codes.
So I might have two customers, looking like:
test custntest addre|test custname |test address |323456|
test custntest addre|test custname2|test address2|123456|
Using the group by, my query will return the following:
test custntest addre|test custname |test address |123456|
Since I'm using min, it's going to give me the minimum value for all of the fields, but not necessarily from the same record. So I've lost my record integrity here - the address and name returned by the query do not correctly match the postal code.
So how do I maintain data integrity on non-grouped fields when using a group by clause?
Hopefully I explained it clearly enough, and thanks in advance for the help.
EDIT: Solved. Thanks everyone!
You can always use ROW_NUMBER instead of GROUP BY
WITH A AS (
SELECT Customer_Summary, customer_name, customer_address, customer_postal_code,
ROW_NUMBER() OVER (PARTITION BY Customer_Summary ORDER BY customer_name, customer_address) AS rn
FROM Order
)
SELECT Customer_Summary, customer_name, customer_address, customer_postal_code
FROM A
WHERE rn = 1
Then you are free to order which customer to use in the ORDER BY clause. Currently I am order them by name and then address.
Edit:
My solution does what you asked for. But I surely agree with the others: If you are allowed to change the database structure, this would be a good idea... which you are not (saw your comment). Well, then ROW_NUMBER() is a good way.
I think you need to re-think your structure.
Ideally you would have a Customer table with an unique ID. Then you would use that unique ID in the Order table. Then you don't need the strange "first 10 characters" method that you are using. Instead, you just group by the unique ID from the Customer table.
You could even then also have a separate table for addresses, relating each address to the customer, with multiple rows (with fields marking them as home address, delivery address, billing address, etc).
This way you separate the Customer information from the Address information and from the Order information. Such that if the customer changes name (marriage) or address (moving home) you don't break your data - Everything is related by the IDs, not the data itself.
[This relationship is known as a Foreign Key.]

How to count unique records and get number of these uniques in table using SQL?

Imagine I have table like this:
id:Product:shop_id
1:Basketball:41
2:Football:41
3:Rocket:45
4:Car:86
5:Plane:86
Now, this is an example of large internet mall, where there are shops which sell to one customer, so customer can choose more products from each shop and buy it in one basket.
However, I am not sure if there is any SQL syntax which allows me to simply get unique shop_ids and total number of those shops' products in customer basket. So I'd get something like:
Shop 41 has 2 products
Shop 45 one product
Shop 86 two product
I can make SQL queries to scoop through table to make some kind of ['shop_id']['number_of_products'] array variable that would store all products' shop_ids, then "unique them" - up and count how many times I had to cut one more shop_id out to have some remaining but that just seems as a lot of useless scripting.
If you got some nice and neat idea, please, let me know.
This is exactly the sort of thing that aggregate functions are for. You make one row of output for each group of rows in the table. Group them by shop_id and count how many rows are in each group.
select shop_id, count(1) from TABLE_NAME
group by shop_id