Excluding records where criteria meets - sql

I have a set of records where we identify several items connected to a customer.
My dilemma is that if a customer has both items then I would like to exclude that customer.
If they only have one specific item then I want to include it.
I will be using this code to create a view so i'm trying to find the best way. I could try Row_number() to identify different records, but I'm not sure that would be ideal in this situation.
Example data table:
Customer | ItemID | value1 | Value2
A 12 35 0
B 12 35 0
C 13 0 25
C 12 0 25
D 18 225 12
Desired Output:
Customer | ItemID | value1 | Value2
A 12 35 0
B 12 35 0
This is what I have so far:
select Customer, ItemID, Value1, Value2
from Table1
where itemID = 12
This would give me customer 'C', which I don't want.

If you want customers who have itemid = 12 but not itemid = 13 you can use NOT EXISTS:
select * from tablename t
where itemid = 12
and not exists (
select 1 from tablename
where customer = t.customer
and itemid = 13
)
If you want customers who have itemid = 12 and not any other itemid:
select * from tablename t
where itemid = 12
and not exists (
select 1 from tablename
where customer = t.customer
and itemid <> 12
)
or:
select * from tablename
where customer in (
select customer from tablename
group by customer
having min(itemid) = 12 and max(itemid) = 12
)

I think you need to clarify your question but, as I understand it, you're looking to return the all rows where:
1) A customer has a particular item (i.e. Item ID 12, which excludes customer D)
and
(2) They only have one item in total, which excludes customer C since they have two items.
If that is the case, then here's what I've got:
SELECT *
FROM Table1
WHERE ItemID == '12' AND
Customer in (
SELECT Customer
FROM Table1
GROUP BY Customer
HAVING Count(Customer) = 1
)
Edit: I clarified my interpretation of OP's question. I also tested my solution on SQL Fiddle (http://sqlfiddle.com/#!5/b5f1f/2/0) and updated the WHERE clause accordingly.

Related

select query - eliminate rows with duplicate column value on condition

I have a select query that ends up with results like:
ID COMPLIANT
------------------
10 0
12 0
29 0
29 1
43 1
44 1
44 0
How can I get results without these duplicate ID rows, on the condition that if an ID has already been marked as COMPLIANT once (a 1 instead of a 0), the duplicate rows with COMPLIANT=0 do not appear? I'd want:
ID COMPLIANT
------------------
10 0
12 0
29 1
43 1
44 1
How about aggregation?
select id, max(complaint) as complaint
from t
group by id;
This returns one row per id. If you can have multiple complaints -- and you want all of those -- than an alternative is:
select id, complaint
from t
where complaint = 1
union all
select id, complaint
from t
where not exists (select 1 from t t2 where t2.id = t.id and t2.complaint = 1);
this will work:
select id, max(complaint)
from tablename
group by id;

Find average amount of times a value appears in table

How do I count the average amount of times a given number appears in a database?
id | ...
----------
1 | ...
5 | ...
2 | ...
3 | ...
3 | ...
1 | ...
6 | ...
4 | ...
3 | ...
...| ...
id corresponds to the id of the user. Perhaps the table is for customer orders or donations made by a user. For the above table:
id 1 = 2 entries
id 2 = 1 entry
id 3 = 3 entries
id 4 = 1 entry
id 5 = 1 entry
id 6 = 1 entry
Average = (2+1+3+1+1+1)/6 = 1.5 entries per user
The average number of orders/donations made per user is 1.5 to give an example.
I could do something like the below:
$getTotalEntries = $db->prepare("
SELECT *
FROM table
");
$getTotalEntries->execute();
$totalEntries = $getTotalEntries->rowCount();
$getGroupedEntries = $db->prepare("
SELECT *
FROM table
GROUP BY id
");
$getGroupedEntries->execute();
$groupedEntries = $getTotalEntries->rowCount();
$average = $totalEntries/$groupedEntries;
I'm hoping for a single SQL request, however. Incidentally, the below gives me the number of occurances of a given id, but I cannot AVG() them.
$getAverageEntries = $db->prepare("
SELECT id, COUNT(*)
FROM table
GROUP BY id
"); // works, returns the 2,1,3,1,... from before
$getAverageEntries = $db->prepare("
SELECT AVG(COUNT(*))
FROM table
GROUP BY id
"); // won't find aggregate count
How about this?
select count(id) / count(distinct id) as avgEntriesPerUser
from table t;
The only issue with this would be a NULL value for id. If this occurred (and I find it highly unlikely for a column named id), then the above ignores those rows entirely. It can be modified to take this situation into account.
select avg(a.entryCount)
from (
select id, count(id) as entryCount
from <tablename>
group by id
) a;
in SQL you need to do a count to get the number of entries
select avg(entries) from(
Select Distinct Id.tableName, count(Id) As Entries
from tableName
group by ID)
You mean?
select avg(countPerID) from (
select id, count(*) as countPerID from table group by id) x

SQL, Check if Rows are in another Table

I have two tables, Stock and Warehouse.
I need to get the Items which are available in all Warehouses.
Here an example:
Stock Table:
ItemID WarehouseID ItemAmount
-------------------------------------------
1043 1 20
1043 2 2
1043 3 16
1043 4 17
1044 1 32
1044 2 12
1044 4 7
1055 2 6
 
Warehouse Table:
WarehouseID WarehouseName
-------------------------------
1 Name1
2 Name2
3 Name3
4 Name4
For the Example the result should be Item 1043 because its available in all Warehouses, unlike the other ones.
I didn't get to a solution, can anyone help me?
You could also use this "double negative" query using NOT EXISTS:
SELECT DISTINCT s.ItemID
FROM StockTable s
WHERE NOT EXISTS
(
SELECT 1 FROM Warehouse w
WHERE NOT EXISTS(SELECT 1 FROM StockTable s2
WHERE w.WarehouseID = s2.WarehouseID
AND s.ItemID = s2.ItemID)
)
Demo fiddle
This approach looks more verbose but it has some benefits:
you can change it easily if the rules are getting more complex
you can remove the DISTINCT to see all rows
you can add all columns since GROUP BY was not used
it has no issues with null values
select itemid
from stock
group by itemid
having count(distinct warehouseid) = (select count(*) from warehouse);
SQLFiddle: http://sqlfiddle.com/#!15/e4273/1
If the stock table may also contain items with an amount = 0 you need to add a where clause:
select itemid
from stock
where itemamount > 0
group by itemid
having count(distinct warehouseid) = (select count(*) from warehouse);
NOT EXISTS combined with EXCEPT:
select distinct ItemID
from stock s1
where not exists (select warehouseid from warehouse
except
select warehouseid from stock s2 where s2.ItemID = s1.ItemID);
You can even replace select distinct ItemID with select * to get all those items.
I use this query:
SELECT
ItemID
FROM
stock
GROUP BY
ItemID
HAVING
SUM(DISTINCT warehouseid) = (SELECT SUM(WarehouseID) from warehouse)
That is more reliable than using COUNT, because in a rare situation of don't making a foreign key it should returns some invalid results.

Need a query to find count of a column record?

I have a table like this,
ProductId CategoryID bIsPrimary
1 5 1
1 6 0
1 7 0
2 18 1
2 19 1
I need a output like this,
ProductID PrimaryCategoryCount
1 1
2 2
Basically i need to find the the number of primary categories for each product.
SELECT ProductId, COUNT(*)
FROM SomeTable
WHERE bIsPrimary <> 0
GROUP BY ProductId
SELECT
ProductId
,sum(case when bIsPrimary = 1 then 1 else 0 end) as PrimaryCategoryCount
from
Table
group by
ProductId
or
SELECT
ProductId
,count(CategoryId)
from
Table
where bIsPrimiary = 1
group by ProductId
Both will provide you the same result. Pick up one which suits you more or is faster.
SELECT ProductId, COUNT(bIsPrimary)
FROM yourTable
GROUP BY ProductId
This is how I'd do it. WHERE clause isn't necessary here if I'm not mistaken.

SQL Selecting rows with multiple values

I have these 2 tables:
Table SW_ITEM:
ID SWID ITEM_ID
1 1 99
2 2 99
3 5 99
4 2 100
5 1 100
6 1 101
7 2 102
Table ITEM:
ID FILENAME
99 abc
100 def
101 geh
102 ijk
column ITEM_ID is a foreign key to the column ID of table ITEM.
So I want all filenames which have the SWID "1" AND "2" (that would be ITEMID 99 and 100, so their filenames are "abc" and "def")
Here I have to say that it is possible that ITEM_ID has more than one entry with the same SWID, so I cannot use this SQL:
SELECT ITEM_ID FROM SW_ITEM
WHERE SWID IN (1,2)
GROUP BY ITEM_ID
HAVING COUNT(ITEM_ID) = 2
So is there any other possibility to get all entries which have the SWID 1 and 2 (creating a join for every SWID is also not an option - because with many entries it would be really slow)
Kind regards
You need to use DISTINCT in COUNT and count SWID instead of ITEM_ID:
SELECT ITEM_ID FROM SW_ITEM
WHERE SWID IN (1,2)
GROUP BY ITEM_ID
HAVING COUNT(DISTINCT SWID) = 2;
Please checkout this demo.
To retrieve all filenames, try:
SELECT ITEM_ID, FILENAME
FROM ITEM JOIN SW_ITEM ON ITEM.ID = SW_ITEM.ITEM_ID
WHERE SWID IN (1,2)
GROUP BY ITEM_ID
HAVING COUNT(DISTINCT SWID) = 2;
Demo
I have a little different problem where I have to find a person with multiple entries in the same table based on email for that the above solution didn't work for me. You can try using the following,
SELECT person_id,
(ROW_NUMBER () OVER (PARTITION BY pers_email ORDER BY pers_name) person_count
from pers_table
WHERE person_count > 2;
Try this hope it works :)