SQL Server / select count users per combination of individual items - sql-server

Users owns licenses, and a plan is a combination of licenses.
Sometimes a user owns an individual license, which is not part of a plan.
I want to count the number of users per plan. In the exemple below, it should return :
PlanName | Number of Users
P1 | 1
P2 | 2
Tables :
Users Licenses Plans
----------------- --------------------- ----------------
Username | UserID LicenseName|LicenseID PlanName|PlanID
user1 | 1 L1 | 1 P1 | 1
user2 | 2 L2 | 2 P2 | 2
user3 | 3 L3 | 3
user4 | 4 L4 | 4
L5 | 5
UsersAndLicenses PlansAndLicenses
---------------- ----------------
UserID | LicenseID PlanID | LicenseID
1 | 1 P1 | 1
1 | 2 P1 | 2
1 | 3 P1 | 3
2 | 4 P2 | 4
2 | 5 P2 | 5
3 | 1
4 | 4
4 | 5
I started with a select to get the list of users and plans (I will apply a count on this select) and I get an issue : user3 who has only L1 (he hasn't a plan) is listed in P1 (L1 is part of P1). My select statement is :
SELECT Plans.PlanName, Users.UserName FROM Users
INNER JOIN (((Licenses INNER JOIN LicensesPlans ON Licenses.LicenseID = LicensesPlans.LicenseID)
INNER JOIN Plans ON LicensesPlans.PlanID = Plans.PlanID)
INNER JOIN UsersLicenses ON Licenses.LicenseID = UsersLicenses.LicenseID) ON Users.UserID = UsersLicenses.UserID
GROUP BY Plans.PlanName, Users.UserName;
What is wrong with my select ?

As you are only making counts of how many users for each plan you could probably get away with joining these two tables
UsersAndLicenses
PlansAndLicenses
and aggregating the number of users for each plan id.
What have you tried so far?

I wasn't able to solve my issue with just a query.
I created a stored procedure which is called for each user.
This stored procedure use a cursor to scroll through all the plans, retrieve the licenses of each plan and compare them (using an EXCEPT statement) with the licences owned by the user.

Related

SQLite3 count how many counts?

I'm learning SQLite3 and having trouble with this particular output.
Let's say I have a column like:
user0
user1
user1
user2
user2
user3
user3
user3
user4
user4
user4
user4
I would like to count how many times a user appears in the column, and having an output like:
1 | 1
2 | 2
1 | 3
1 | 4
Meaning: There is 1 user appearing 1 time, 2 users appearing 2 times, 1 user appearing 3 times, 1 user appearing 4 times.
I don't need to know anything else, only how many users are with how many accounts.
You group by once to get the counters of the 1st column you need and then again on this result:
select count(*) total, counter
from (
select count(*) counter
from tablename
group by col
)
group by counter
See the demo
Results:
| total | counter |
| ----- | ------- |
| 1 | 1 |
| 2 | 2 |
| 1 | 3 |
| 1 | 4 |
Here is a small example using GROUP BY and the COUNT()-function.
Im using MSSQL, but it should be nearly the same in SQLITE3.
This is the table Im using:
select count([name]), [name] from Test
group by [name]
This is the result:

SQL Server delete on multiple foreign keyed tables - performance

I am trying to remove old data from a SQL Server database, given a list of ID's, but I'm trying to figure out how to get it to run faster. Currently deleting a list of 250 ID's takes around 1 hour. These ID's are attached to our 'root' objects, example below. Each of these has foreign key constraints.
Products
| productID | description | price |
+-----------------+-------------------+-------------+
| 1 | item 1 | 5.00 |
| 2 | item 2 | 5.00 |
| 3 | item 3 | 5.00 |
| ... | ... | ... |
Sales
| saleID | productID |
+-----------------+-------------------+
| 4 | 1 |
| 5 | 2 |
| 6 | 3 |
| ... | ... |
Taxes
| taxID | saleID |
+-----------------+-------------------+
| 7 | 4 |
| 8 | 5 |
| 9 | 6 |
| ... | ... |
Currently, we are just passing a list of product ID's and cascading through manually, such as
DECLARE #ProductIDsRemoval AS TABLE { id int }
INSERT INTO #ProductIDsRemoval VALUES (1)
DELETE t
FROM dbo.Taxes t
INNER JOIN dbo.Sales s ON (s.saleID = t.saleID)
INNER JOIN #ProductIDsRemoval p ON (s.productID = p.id)
DELETE s
FROM dbo.Sales s
INNER JOIN #ProductIDsRemoval p ON (s.productID = p.id)
DELETE p
FROM dbo.Products p
INNER JOIN #ProductIDsRemoval p2 ON (p.productID = p2.id)
This works fine, however my issue is that my table structure has ~70 tables and at least a couple thousand rows in each to remove, if not a couple million. Currently, my query takes anywhere from 1 to 6 hours to run, depending on the number of base ID's we're removing (my structure doesn't actually use Products/Taxes/Sales, but it's a decent analogy, and the number we're aiming to remove is ~750 base ids, which we are estimating 3-5 hours for runtime)
I've seen other Stack Overflow answers saying to drop all constraints, add the on-cascade delete, and then re-add the constraints, but this also is taking quite a long time, as I would need to 1. Drop constraints. 2. Rebuild with on-cascade. 3. run my query. 4 drop constraints. 5 re-add without on-cascade.
I've also been looking at possibly just selecting everything I need into temp tables, truncating all of the other tables, and then re-inserting all of my values back and re-setting the indexes based on the last item I added, but again I would need to edit all foreign keys, which I would prefer to not do.

Many-to-many that shows columns with null

I'm having some difficulty trying to figure how to adjust my query. I'm not very good at SQL queries as it's not my forte. Anyway, I'm not sure what I'm doing wrong. Here's my table setup.
ID | Customer
---+-------------
1 | John
2 | Jane
3 | Steve
ID | Assets
---+-------------
1 | RealEstate
2 | Currency
3 | Stocks
CustomerID | AssetConfigurationId | Status
-----------+----------------------+-------
1 | 1 | E
1 | 2 | F
1 | 3 | X
2 | 3 | X
And if I query customer = 3, I want to get the following
AssetConfigurationId | Status
---------------------+------------
1 | null
2 | null
3 | X
Currently have this. I'm trying to understand how I can use left join to show all the assets and just have the values of the statuses to null for a specific customer. Right now it only shows the 3rd row. Trying to do this in a SQL Server stored procedure so that my .net application can get a list of the assets already and I'll just modify the statuses when it comes to converting them to objects.
select
ac.Id,
r.Status
from
assets ac
left join
assets_ref r on r.AssetConfigurationId = ac.Id
where
r.CustomerID = 3
Move your WHERE condition in the inner query.
select
ac.Id,
r.Status
from assets ac
left join
(select * from assets_ref where CustomerID = 3) r
on r.AssetConfigurationId = ac.Id;
You can use multiple conditions in JOINs:
select
ac.Id,
r.Status
from assets ac
left join assets_ref r
on r.AssetConfigurationId = ac.Id
and CustomerID = 3;

SQL Query to get data based on multiple filters

I have following Product table and ProductTag tables -
ID | Product
--------------
1 | Product_A
2 | Product_B
3 | Product_C
TagID | ProductID
----------------------
1 | 2
1 | 3
2 | 1
2 | 2
2 | 3
3 | 1
3 | 2
Now I need a SQL query that return all products list which are having both Tag 1 and 2. Result should be as given below -
ProductID | Product
------------------------
2 | Product_B
3 | Product_C
Please suggest how can i write a MS SQL query for this.
SELECT p.ID, p.Product
FROM Product p
INNER JOIN ProductTag pt
ON p.ID = pt.ProductID
WHERE pt.TagID IN (1, 2) -- <== Tags you want to find
GROUP BY p.ID, o.Product
HAVING COUNT(*) = 2 -- <== tag count on WHERE clause
however, if TagID is not unique on every Product, you need to count only the distinct product.
HAVING COUNT(DISTINCT pt.TagID) = 2
More on: SQL of Relational Division

How to show data from joined table if join id does not exist in joined table?

I have the following tables with corresponding data:
RequestsTable
ReqID | ReqBy | ReqDate
1 | User1 | 30/04/2013
2 | User2 | 30/04/2013
3 | MasterUser | 30/04/2013
and
RequestorsTable
ReqUserName | ReqFullName
User1 | Sample User 1
User2 | Sample User 2
and i need the output to be like this:
ReqID | ReqBy | ReqDate
1 | Sample User 1 | 30/04/2013
2 | Sample User 2 | 30/04/2013
3 | MasterUser | 30/04/2013
May I know how can I do this^^?
Just a quick note: MasterUser is a superuser account and is therefore not in the database, but could make requests.
Thanks for the help.
Using ISNULL and a LEFT OUTER JOIN allows you to selectively choose what column to return in the resultset.
If a matching record is found in RequestorsTable, use that value
otherwise, use the ReqBy value from RequestsTable
Sample Select statement
SELECT rt.ReqID, ISNULL(rst.ReqFullName, rt.ReqBy), rt.ReqDate
FROM RequestsTable rt
LEFT OUTER JOIN RequestorsTable rst ON rst.ReqUserName = rt.ReqBy
An alternative to ISNULL would be COALESCE wich more or less performs similar functionality.
Sample Select statement
SELECT rt.ReqID, COALESCE(rst.ReqFullName, rt.ReqBy), rt.ReqDate
FROM RequestsTable rt
LEFT OUTER JOIN RequestorsTable rst ON rst.ReqUserName = rt.ReqBy
Try this one -
SELECT
rt.ReqID
, ReqBy = ISNULL(rt2.ReqFullName, rt.ReqBy)
, rt.ReqDate
FROM dbo.RequestsTable rt
LEFT JOIN dbo.RequestorsTable rt2 ON rt.ReqBy = rt2.ReqUserName

Resources