SQL set column with derived value - sql

How to I set a column value to "1" where sum(price) >= 88
Eg: if sum(price) for customer A is more than or equal than 88, delivery will be updated as "Y"
customer table: name, price, delivery (i only listed out the important column)
My SQL query:
UPDATE customer
SET delivery ='Y'
WHERE (SELECT SUM(price) FROM customer
GROUP BY name )>=88;
Error: You can't specify target table "customer" for update in FROM clause
See attached table:

You don't have any join between the table you're updating and your subselect. Your table doesn't really have a key, but given what you've shown I'd do it like:
update Orders
set delivery = 'Y'
where Name in (SELECT NAME FROM
(SELECT NAME, SUM(PRICE) TOTAL_PRICE
FROM ORDERS
GROUP BY NAME
HAVING SUM(PRICE)> 88) A
)
So basically it gets the Name of each customer and the sum or their order prices but only where the sum is over 88. It then just pulls out the Name from that subselect and only updates the rows where the name exists in the list of names for orders that sum over 88.

Set the column to the value that you selecting from the table and don't group them.
SET COLUMN_X = (SELECT SUM(price) FROM customer)
Now you use your query and it looks cleaner
UPDATE customer
SET delivery ='Y'
WHERE COLUMN_X = >=88;

You don't say which database you are using (or which version of it), so you may try if this standard SQL works on it:
with
x as (
select name
from customer
group by name
having sum(price) > 88
)
update customer c
set delivery = 'Y'
where name in (select name from x)

Essentially the same thing #TheImpaler just posted, but with a subquery instead of a CTE.
Get the names that qualify first, then join to that list for the UPDATE statement.
UPDATE t
SET delivery = 'Y'
FROM
#t AS t
JOIN
(
SELECT
name
,SUM(price) AS total
FROM
#t
GROUP BY
name
HAVING
SUM(price) >= 88
) AS d
ON
d.name = t.name;
Here's a working Rextester: https://rextester.com/LQWE88535

Related

SQL Count of two values in one column

I have a table with customer name and Status columns. The status column has two values
Test
Live
The customers appear more than once and can be classed as either test, live or BOTH like below:
**Customer | Status**
Logistics | Test
Logistics | Live
Ample | Live
What I want is a query to give me a count of the number of distinct customers who fall under both statuses. So using the above table, I would count customer logistics (since it has both test and live) but not ample (since it is just live).
Any ideas?
You can use group by clause :
select Customer, count(*)
from table t
group by Customer
having min(status) <> max(status);
If you want it with specific status then include where clause :
select Customer, count(*)
from table t
where status in ('Test', 'Live')
group by Customer
having count(distinct status) = 2;
EDIT : If you want other columns too, then i would prefer :
select t.*
from table t
where exists (select 1 from table t1 where t1.Customer = t.Customer and t1.status <> t.status);
try something like this:
select customer
from
(select customer, max(IsTest) as IsTest , max(IsLive) as IsLive
from
(select customer,
case when status='test' then 1 else 0 end as IsTest,
case when status='live' then 1 else 0 end as IsLive
from table) a
group by customer) b
where IsTest = 1 and IsLive = 1
you can use group by clause to get your desire output.
select Customer, count(*)
from table t
group by Customer
having min(status) <> max(status);

SQL querying a customer ID who ordered both product A and B

Having a bit of trouble when trying to figure out how to return a query of a customer who ordered both A and B
What I'm looking for is all customers who order both product A and product B
SELECT CustomerID
FROM table
WHERE product in ('a','b')
GROUP BY customerid
HAVING COUNT(distinct product) = 2
I don't normally post code only answers but there isn't a lot that words can add to this- the query predominantly explains itself
You can also
HAVING max(product) <> min(product)
It may be worth pointing out that in queries, the WHERE is performed, filtering to just products A and B. Then the GROUP BY is performed, grouping customer and counting the distinct number of products (or getting the min and max). Then the HAVING is performed, filtering to just those with 2 distinct products (or getting only those where MIN i.e. A, is different to MAX i.e. B)
If you'v never encountered HAVING, it is logically equivalent to:
SELECT CustomerID
FROM(
SELECT CustomerID, COUNT(distinct product) as count_distinct_product
FROM table
WHERE product in ('a','b')
GROUP BY customerid
)z
WHERE
z.count_distinct_product = 2
In a HAVING clause you can only refer to columns that are mentioned in the group by. You can also refer to aggregate operations (such as count/min/max) on other columns not mentioned in the group by
I have never worked with SQLLite, but since it's specs say it is a Relational Database, it should allow the following query.
select CustomerID
from table t
where exists (
select *
from table
where CustomerID = t.CustomerID
and Product = 'A'
)
and exists (
select *
from table
where CustomerID = t.CustomerID
and Product = 'B'
)
I'd use a correlated sub-query with a HAVING clause to scoop in both products in a single WHERE clause.
SELECT
t.Customer
FROM
#t AS t
WHERE
EXISTS
(
SELECT
1
FROM
#t AS s
WHERE
t.Customer = s.Customer
AND s.Product IN ('A', 'B')
HAVING
COUNT(DISTINCT s.Product) = 2
)
GROUP BY
t.Customer;
Select customerid from table group by customerid having product like 'A' and product like 'B' or
you can try having count(distinct product) =2this seems to be more accurate.
The whole idea is in a group of customerid suppose 1 if I have several A's and B's count(distinct product) will give as 2 else it will be 1 so the answer is as above.
Another way I just figured out was
SELECT CustomerID
FROM table
WHERE product in ('a','b')
GROUP BY customerid
HAVING sum(case product ='a' then 1 else 0 end) > 0
and sum(case when product ='b' then 1 else 0 end) > 0

select multiple records based on order by

i have a table with a bunch of customer IDs. in a customer table is also these IDs but each id can be on multiple records for the same customer. i want to select the most recently used record which i can get by doing order by <my_field> desc
say i have 100 customer IDs in this table and in the customers table there is 120 records with these IDs (some are duplicates). how can i apply my order by condition to only get the most recent matching records?
dbms is sql server 2000.
table is basically like this:
loc_nbr and cust_nbr are primary keys
a customer shops at location 1. they get assigned loc_nbr = 1 and cust_nbr = 1
then a customer_id of 1.
they shop again but this time at location 2. so they get assigned loc_nbr = 2 and cust_Nbr = 1. then the same customer_id of 1 based on their other attributes like name and address.
because they shopped at location 2 AFTER location 1, it will have a more recent rec_alt_ts value, which is the record i would want to retrieve.
You want to use the ROW_NUMBER() function with a Common Table Expression (CTE).
Here's a basic example. You should be able to use a similar query with your data.
;WITH TheLatest AS
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY group-by-fields ORDER BY sorting-fields) AS ItemCount
FROM TheTable
)
SELECT *
FROM TheLatest
WHERE ItemCount = 1
UPDATE: I just noticed that this was tagged with sql-server-2000. This will only work on SQL Server 2005 and later.
Since you didn't give real table and field names, this is just psuedo code for a solution.
select *
from customer_table t2
inner join location_table t1
on t1.some_key = t2.some_key
where t1.LocationKey = (select top 1 (LocationKey) as LatestLocationKey from location_table where cust_id = t1.cust_id order by some_field)
Use an aggregate function in the query to group by customer IDs:
SELECT cust_Nbr, MAX(rec_alt_ts) AS most_recent_transaction, other_fields
FROM tableName
GROUP BY cust_Nbr, other_fields
ORDER BY cust_Nbr DESC;
This assumes that rec_alt_ts increases every time, thus the max entry for that cust_Nbr would be the most recent entry.
By using time and date we can take out the recent detail for the customer.
use the column from where you take out the date and the time for the customer.
eg:
SQL> select ename , to_date(hiredate,'dd-mm-yyyy hh24:mi:ss') from emp order by to_date(hiredate,'dd-mm-yyyy hh24:mi:ss');

Select a NON-DISTINCT column in a query that return distincts rows

The following query returns the results that I need but I have to add the ID of the row to then update it. If I add the ID directly in the select statement it will return me more results then I need because each ID is unique so the DISTINCT statement see the line as unique.
SELECT DISTINCT ucpse.MemberID, ucpse.ProductID, ucpse.UserID
FROM UserCustomerProductSalaryExceptions as ucpse
WHERE EXISTS (SELECT NULL
FROM UserCustomerProductSalaryExceptions as upcse2
WHERE ucpse.userid = upcse2.userid AND ucpse.MemberID = upcse2.MemberID AND ucpse.ProductID = upcse2.ProductID
GROUP BY upcse2.UserID, upcse2.memberid, upcse2.productid
HAVING COUNT(UserID) >= 2
)
So basically I need to add ucpse.ID in the Select statement while keeping DISTINCT values for MemberID,ProductID and UserID.
Any Ideas ?
Thank you
According to you comment:
If the data has been duplicated 67 times for a given employee with a given product and a given client, I need to keep only one of thoses records. It's not important which one, so this is why I use DISTINC to obtain unique combinaison of given employee with a given product and a given client.
You can use MIN() or MAX() and GROUP BY instead of DISTINCT
SELECT MAX(ucpse.ID) AS ID, ucpse.MemberID, ucpse.ProductID, ucpse.UserID
FROM UserCustomerProductSalaryExceptions as ucpse
WHERE EXISTS (SELECT NULL
FROM UserCustomerProductSalaryExceptions as upcse2
WHERE ucpse.userid = upcse2.userid AND ucpse.MemberID = upcse2.MemberID AND ucpse.ProductID = upcse2.ProductID
GROUP BY upcse2.UserID, upcse2.memberid, upcse2.productid
HAVING COUNT(UserID) >= 2
)
GROUP BY ucpse.MemberID, ucpse.ProductID, ucpse.UserID
UPDATE:
From you comments I think the below query is what you need
DELETE FROM UserCustomerProductSalaryExceptions
WHERE ID NOT IN ( SELECT MAX(ucpse.ID) AS ID
FROM #UserCustomerProductSalaryExceptions
GROUP BY ucpse.MemberID, ucpse.ProductID, ucpse.UserID
HAVING COUNT(ucpse.ID) >= 2
)
If all you want is to delete the duplicates, this will do it:
WITH X AS
(SELECT ID,
ROW_NUMBER() OVER (PARTITION BY MemberID, ProductID, UserID ORDER BY ID) AS DupRowNum<br
FROM UserCustomerProductSalaryExceptions
)
DELETE X WHERE DupRowNum > 1
ID's not necessary - try:
UPDATE uu SET
<your settings here>
FROM UserCustomerProductSalaryExceptions uu
JOIN ( <paste your entire query above here>
) uc ON uc.MemberID=uu.MemberId AND uc.ProductID=uu.ProductId AND uc.UserID=uu.UserId
From the sound of your data structure (which I would STRONGLY advise normalizing as soon as possible), it sounds like you should be updating all the records. It sounds as if each duplicate is important because it contains some information about an employee's relation to a customer or product.
I would probably update all the records. Try this:
UPDATE UCPSE
SET
--Do your updates here
FROM UserCustomerProductSalaryExceptions as ucpse
JOIN
(
SELECT UserID, MemberID, ProductID
FROM UserCustomerProductSalaryExceptions
GROUP BY UserID, MemberID, ProductID
HAVING COUNT(UserID) >= 2
) T
ON ucpse.UserID = T.UserID AND ucpse.MemberID = T.MemberID AND ucpse.ProductID = T.ProductID

Using COUNT with MAX in SQL

I am trying to find which customer has the most transactions. Transaction table has an foreign key that identifies each transaction with a customer. What I currently is the following code:
WITH Customers as (
SELECT
[CustName] as 'Customer',
[TRANSACTION].[CustID] as 'Total # of Transactions'
FROM [dbo].[CUSTOMER]
INNER JOIN [dbo].[TRANSACTION]
ON [CUSTOMER].[CustID] = [TRANSACTION].[CustID]
)
SELECT *
FROM Customers
WHERE 'Total # of Transactions' = (SELECT MAX('Total # of Transactions') FROM Customers);
Two things are wrong:
1) The latter part of the code doesn't accept 'Total # of Transactions'. If I were to rename it to a single word, I could treat it kind of like a variable.
2) My last SELECT statement gives me a result of the customer and all their transactions, but doesn't give me a COUNT of those transactions. I'm not sure how to use COUNT in conjunction with MAX.
First select customers and transaction count.
Then select the largest one.
Them limit your select to that item.
Work you way from the inside out.
SELECT *
FROM Customers
WHERE CustID =
(
SELECT TOP 1 CustID
FROM (SELECT CustID, COUNT(*) AS TCOUNT
FROM TRANSACTIONS
GROUP BY CustID) T
ORDER BY T.TCOUNT DESC
) TT
This should get you everything you need. To get the top customer just add Top 1 to the select
WITH Customers as (
SELECT
[CustName] as Name
FROM [dbo].[CUSTOMER]
INNER JOIN [dbo].[TRANSACTION]
ON [CUSTOMER].[CustID] = [TRANSACTION].[CustID]
)
-- to get count of transactions
Select Count(*) as count, Name
FROM Customers
Group by Name
Order By Count(*) desc
Your inner table just returns CustID as a total number of transactions? You need to start by finding the total count for each customer. Also for a column you can use [Name], when you use apostrophes it thinks you are comparing a string. If you want to return all customers with the highest count, you could use this:
WITH TransactionCounts as (
SELECT
CustID,
COUNT(*) AS TransactionCount
FROM [dbo].[TRANSACTION]
GROUP BY CustID
)
SELECT TOP 1 CUSTOMER.*, TransactionCount
FROM TransactionCounts
INNER JOIN CUSTOMER ON CUSTOMER.CustID = TransactionCounts.CustId
ORDER BY TransactionCount DESC
-- alternate to select all if multiple customers are tied for highest count
--WHERE TransactionCount = (SELECT MAX(TransactionCount) FROM TransactionCounts)