PostgreSql - How to create conditional column with the filter on another column? - sql

I want to add 1 more columns where segment out whether the customer had sold at least one product or not.
Data example:
ProductID Customer Status
1 John Not sold
2 John Not Sold
3 John Sold
My expect result
ProductID Customer Status Sold_at_least_1
1 John Not sold Yes
2 John Not Sold Yes
3 John Sold Yes
4 Andrew Not Sold No
5 Andrew Not Sold No
6 Brandon Sold Yes
This is an example data. Sorry for any inconvenience as I unable to extract data out. Btw, appreciating for any helps.

You can do a window count of records of the same customer that have status = 'Sold' in a case expression:
select
t.*,
case when sum( (status = 'Sold')::int ) over(partition by customer) >= 1
then 'Yes'
else 'No'
end
from mytable
NB: note that this does not magically create new records (as shown in your sample data). This query gives you as many records in the resultset as there are in the table, with an additionnal column that indicates whether each cutsomer has at least one sold item in the table.
Here is a demo provided by VBokšić (thanks).

Another option is to use bool_or() as a window function. If you can live with a boolean column rather than a varchar with Yes/No, this makes the expression even simpler:
select productid, customer, status,
bool_or(status = 'Sold') over (partition by customer) as sold_at_least_one
from mytable;
Online example: https://rextester.com/NDN54253

Related

SQL statement, check if other rows have the same value

I have a SQL statement that imports my product inventory from a Access.MDB file. The select statement is below. (well a portion of it)
SELECT
Brand, DESCRIPTION AS Model,
SECONDDESCRIPTION AS Description,
PRODUCT AS [Product Code], TYPE AS Batch, INACTIVE,
CORE AS [Core Range],
IIF([CUSTORD] IS NULL, ROUND(ON_HAND), (IIF(TYPE = 'DISP',ROUND(ON_HAND),ROUND(ON_HAND)-CUSTORD))) AS SOH
You may notice that the select statement will minus any items that are on a customer order from the SOH values. for clarity below is the line that does just that.
IIF([CUSTORD] IS NULL, ROUND(ON_HAND), (IIF(TYPE = 'DISP',ROUND(ON_HAND),ROUND(ON_HAND)-CUSTORD))) AS SOH
The problem i have is, that 1 product code, can have multiple batches, and if an item only has a qty of 1 in each batch, and then the customer order column also contains a 1, this results in 1 - 1 = 0.
However the customer orders column is really indicating that only 1 of the product codes in on a customer order, not that specific batch.
Is there a way to check if that product code has already been "Selected" and has a Customer Order Qty against it and if it does then ignore the customer order qty against this next batch in the table?
To help explain it a little here is a rough idea of the table that would be imported.
Product
Batch_Number
ON_HAND
CUSTORD
Apples
123456
5
1
Apples
234567
1
1
Apples
587554
1
1
Bananas
1548777
1
0
so in the table above with my existing select statement, my results would be
Apples 4 in batch 123456
Bananas 1 in batch 1548777
As the next two lines of apples would actually end up with a value of 0 in batches 234567 and 587554
my program is set to then only return to the user values of items they can sell with a SOH qty > 0
so i need the final datatable in my program to look like this:
Product
Batch_Number
ON_HAND
CUSTORD
Apples
123456
5
1
Apples
234567
1
0
Apples
587554
1
0
Bananas
1548777
1
0
In my table Batch Number is the unique identifier and does not occur twice in the table.
Im working in VB.NET so if it could not be done in the SQL select statement i could be open to the idea of adjusting the values in the dataset datatable, however that would probably be made harder by the fact that the SQL Select statement i'm using never actually imports the CUSTORD column of data into my datatable. As i was trying to handle the SOH values directly at the select statement level.
Hope i have not confused anyone, and explained it as simple as possible.
I have no idea what your initial code has to do with the question. But let me assume that you have a table in the format shown in the question and you want to set on_hand to 0 for all but the first row for each product. You can use:
select product, batch_number, custord,
iif( t.batch_number = (select top 1 t2.batch_number
from t as t2
where t2.product = t.product
order by t2.on_hand desc, t2.batch_number
),
t.on_hand, 0
) as adjusted_on_hand
from t
order by product, on_hand desc, batch_number

Total Sum SQL Server

I have a query that collects many different columns, and I want to include a column that sums the price of every component in an order. Right now, I already have a column that simply shows the price of every component of an order, but I am not sure how to create this new column.
I would think that the code would go something like this, but I am not really clear on what an aggregate function is or why I get an error regarding the aggregate function when I try to run this code.
SELECT ID, Location, Price, (SUM(PriceDescription) FROM table GROUP BY ID WHERE PriceDescription LIKE 'Cost.%' AS Summary)
FROM table
When I say each component, I mean that every ID I have has many different items that make up the general price. I only want to find out how much money I spend on my supplies that I need for my pressure washers which is why I said `Where PriceDescription LIKE 'Cost.%'
To further explain, I have receipts of every customer I've worked with and in these receipts I write down my cost for the soap that I use and the tools for the pressure washer that I rent. I label all of these with 'Cost.' so it looks like (Cost.Water), (Cost.Soap), (Cost.Gas), (Cost.Tools) and I would like it so for Order 1 it there's a column that sums all the Cost._ prices for the order and for Order 2 it sums all the Cost._ prices for that order. I should also mention that each Order does not have the same number of Costs (sometimes when I use my power washer I might not have to buy gas and occasionally soap).
I hope this makes sense, if not please let me know how I can explain further.
`ID Location Price PriceDescription
1 Park 10 Cost.Water
1 Park 8 Cost.Gas
1 Park 11 Cost.Soap
2 Tom 20 Cost.Water
2 Tom 6 Cost.Soap
3 Matt 15 Cost.Tools
3 Matt 15 Cost.Gas
3 Matt 21 Cost.Tools
4 College 32 Cost.Gas
4 College 22 Cost.Water
4 College 11 Cost.Tools`
I would like for my query to create a column like such
`ID Location Price Summary
1 Park 10 29
1 Park 8
1 Park 11
2 Tom 20 26
2 Tom 6
3 Matt 15 51
3 Matt 15
3 Matt 21
4 College 32 65
4 College 22
4 College 11 `
But if the 'Summary' was printed on every line instead of just at the top one, that would be okay too.
You just require sum(Price) over(Partition by Location) will give total sum as below:
SELECT ID, Location, Price, SUM(Price) over(Partition by Location) AS Summed_Price
FROM yourtable
WHERE PriceDescription LIKE 'Cost.%'
First, if your Price column really contains values that match 'Cost.%', then you can not apply SUM() over it. SUM() expects a number (e.g. INT, FLOAT, REAL or DECIMAL). If it is text then you need to explicitly convert it to a number by adding a CAST or CONVERT clause inside the SUM() call.
Second, your query syntax is wrong: you need GROUP BY, and the SELECT fields are not specified correctly. And you want to SUM() the Price field, not the PriceDescription field (which you can't even sum as I explained)
Assuming that Price is numeric (see my first remark), then this is how it can be done:
SELECT ID
, Location
, Price
, (SELECT SUM(Price)
FROM table
WHERE ID = T1.ID AND Location = T1.Location
) AS Summed_Price
FROM table AS T1
to get exact result like posted in question
Select
T.ID,
T.Location,
T.Price,
CASE WHEN (R) = 1 then RN ELSE NULL END Summary
from (
select
ID,
Location,
Price ,
SUM(Price)OVER(PARTITION BY Location)RN,
ROW_number()OVER(PARTITION BY Location ORDER BY ID )R
from Table
)T
order by T.ID

SQL - find prior string value

I have a DB which 'tracks' the customer shopping journey. What I want to do is recall the previous value if their final destination or 'shop' is a particular value.
For example say the shops are named like this:
Shop 1
Shop 2
Shop 3
Shop 4
If my select query returns Shop 4 (for any customer) then I want the extra column to show the previous shop they last shopped at. There is no natural order to my data so I can't literally state that Shop 4 = Shop 3 it just needs to return whatever shop they last shopped at if the last one is Shop 4 (there previous shop could be any 'shop').
This is what I have so far but it's probably way off the mark. I have a date column in my table but don't know how to use it in this way.
Select ...
case
when TableShop.ShopName LIKE 'Shop4' then
cast(TableShop.ShopName -1 AS nvarchar(50))
end
From ...
Presumably, you have some column that specifies the ordering of the visits -- say a visitDatetime column.
Then, you can use the ANSI standard LAG() function:
select s.*,
(case when s.shopName = 'Shop4'
then lag(s.shopName) over (partition by customerId order by visitDateTime)
end) as prev_ShopName
from tableshop s;

Extract id along with count of other columns

I have four columns in my table
CUSTOMER, TRANSACTION(UNIQUE) PRODUCTA PRODUCTB
Product A or Product B is either 0 or 1 depending on the item bought. Both are not equal to 1 as each row corresponds to a transaction and it is either A or B.
Now I want to extract data such that each customer is listed along with the count of no of product A purchases and product B purchases he made.
select customer,count(PRODUCTA),count(PRODUCTB) from rm_saicharan_final6 group by customer
Its returning all the count including the 0s.
CUSTOMER PRODUCTA PRODUCTB
-------- -------- ---------
32444 209 209
But I want only the count having value=1 not all
Just use SUM as follows:
select customer,SUM(PRODUCTA),SUM(PRODUCTB)
from rm_saicharan_final6 group by customer
SQLFiddle: http://sqlfiddle.com/#!4/ee7da/596
Not entirely sure if this will work but, if it doesnt, it may help
select customer,count(PRODUCTA),count(PRODUCTB) from rm_saicharan_final6 group by customer where PRODUCTA>0 and PRODUCTB>0

Need help in understanding a SELECT query

I have a following query. It uses only one table (Customers) from Northwind database.
I completely have no idea how does it work, and what its intention is. I hope there is a lot of DBAs here so I ask for explanation. particularly don't know what the OVER and PARTITION does here.
WITH NumberedWomen AS
(
SELECT CustomerId ,ROW_NUMBER() OVER
(
PARTITION BY c.Country
ORDER BY LEN(c.CompanyName) ASC
)
women
FROM Customers c
)
SELECT * FROM NumberedWomen WHERE women > 3
If you needed the db schema, it is here
This function:
ROW_NUMBER() OVER (PARTITION BY c.Country ORDER BY LEN(c.CompanyName) ASC)
assigns continuous row numbers to records within each country, ordering the records by LEN(companyName).
If you have these data:
country companyName
US Apple
US Google
UK BAT
UK BP
US GM
, then the query will assign numbers from 1 and 3 to the US companies and 1 to 2 to UK companies, ordering them by the name length:
country companyName ROW_NUMBER()
US GM 1
US Apple 2
US Google 3
UK BP 1
UK BAT 2
ROW_NUMBER() is a ranking function.
OVER tells it how to create rank numbers.
PARTITION BY [expression] tells the ROW_NUMBER function to restart ranking whenever [expression] contains a new value
In your case, for every country, a series of numbers starting with 1 is created. Within a country, the Companies are ordered by the length of their name (shorter name = lower rank).
The final query:
SELECT * FROM NumberedWomen WHERE women > 3
selects all customers except if the company-country combination is part of one of the companies with the 3 shortest names in the same country.