Modifying an existing statement for SQL server - sql-server

I have 2 databases on the same server. We will call our first database ActiDB and the second one will be DomiDB. There is a table on DomiDB called DomiActV7 which we will be working on. In DomiActV7 there is a column called Domain and another column that is empty called NumOfClicks.
Now lets take a look at the other database called ActiDB. There are 2 tables there that are important to us. First table is called ActV7 and the second one is called SeV7.
What I need to do is to get the NumOfClicks that is in the table SeV7 (Database ActiDB) in the other database (DomiDB) combined together for each domain and stored in the empty column called NumOfClicks.
Explaining upper text "Combined together for each domain": There is a list of emails in ActV7 and there is a list of domains in DomiActV7. Domain list should check ActV7 emails and find matching domain (ex.: test#testdomain.com is our email and testdomain.com is our domain. The query found the email with same domain in that email and will now use that rows ID that will connect us to the other table in the same database called SeV7) The ID we can get from that row where the email with maching domain is, is called SeV7OID. With this ID we can get into the other table SeV7 where we can find NumOfClicks.
Now that we found our way to NumOfClicks we need to somehow merge them together since there will be more emails with the same domain in previous tables which means that it will give us more IDs that will lead to more NumOfClicks.. I need to imput combined number of clicks back to the 1st table DomiActV7, also there is a specific number 65535 that should be treated as number 1. That is already implemented in the existing query tho.
The query that is made should be updated to look in ActV7 for Email then get the SeV7OID ID and with that ID it should find the NumOfclicks and input them in the other database. I hope I didn't complicate things too hard but I'm trying to explain the situation as good as I can. Also here is a link to older post with a simillar question: Link to the post
Every Columns data type is varchar(50), only SeV7OID and other IDs are INT.
What is wrong with this query that it does direct Email search in SeV7 which is wrong because there is more missing emails than there
is written in that table that is why we have to check from ActV7
which has all the emails stored.
Existing query that needs to be modified:
UPDATE DomiDB..DomiActV7 SET NumOfClicks = a.NumOfClicks FROM
DomiDB..DomiActV7 d JOIN (SELECT Domain, SUM(CASE WHEN e.NumOfClicks
= 65535 THEN 1 ELSE
e.NumOfClicks END) AS NumOfClicks FROM DomiDB..DomiActV7 d JOIN ActIDB..nact.SeV7 e ON '#'+d.Domain =
right(e.Email,len(d.domain)+1)) a ON a.Domain=d.domain
Example tables: DomiActV7 from DomiDB
+-----------------+-----------------+
| Domain | NumOfClicks |
+-----------------+-----------------+
|thisisadomain.net| |
+-----------------+-----------------+
| moreexamples.com| |
+-----------------+-----------------+
...............
ActV7 from ActiDB
+--------------------+-----------------+
| Email | SeV7OID (key) |
+--------------------+-----------------+
|example#examail.com | 1 |
+--------------------+-----------------+
|a#moreexamples.com | 3 |
+--------------------+-----------------+
.............
SeV7 from ActiDB
+--------------------+-----------------+
| SeV7OID (key) | NumOfClicks |
+--------------------+-----------------+
| 3 | 41 |
+--------------------+-----------------+
| 4 | 22 |
+--------------------+-----------------+
| 1 | 65535 |
+--------------------+-----------------+
..................

Does this produce expected result?
UPDATE d SET d.NumOfClicks = a.NumOfClicks
FROM DomiDB..DomiActV7 d JOIN
( SELECT d.domain,
SUM(CASE
WHEN e.NumOfClicks = 65535 THEN 1
ELSE e.NumOfClicks END) AS NumOfClicks
FROM
DomiDB..DomiActV7 d1
JOIN ActIDB..Actv7 aa ON '#'+d1.domain = right(aa.Email,len(d1.domain)+1)
JOIN ActIDB..SeV7 e ON e.SeV7OID = aa.SeV7OID
group by d1.domain
) a ON a.domain=d.domain

Related

DML trigger, creating new rows for several customers after insert/update/delete

I am using SQL Server 2017.
What I want to achieve: The database contains tables for customers, coupon codes and suppliers. A customer is a company. A customer may be an independent company, it may belong to another company, and it may have "child companies" (the connection between child and parent companies are established through a ParentID column in the customers table).
Coupon codes are related to customers. When a "parent company" activates access a coupon code, the code is supposed to become available for all the parent company's child companies. Likewise, if a parent company deactivates access to a coupon code, child companies should also lose access. As of today, this has to be fixed manually. I am writing a trigger that will take over this manual task.
A table CompanyCodes stores the relationship between companies and coupon codes. Column "Customer" stores the company ID number, column "Supplier" stores the ID of the supplier of the coupon code and column "Code" stores the coupon code.
Whenever a row is inserted, updated or deleted into/from the CompanyCodes table, the trigger first checks whether or not the relevant customer has any child companies. If it does, the trigger is supposed to insert/update/delete a row for each of the company's child companies, so that the changes are reflected in all of them.
E.g. customer 1 is the parent of customer 2 and customer 3. Customer 1 activates access to a new coupon code for supplier X, and a new row is inserted into CompanyCodes with the relevant information. In this case, the trigger is supposed to insert two new rows into the table, one for customer 2 and one for customer 3, where all the information (except the company ID) is the same as what was inserted for customer 1.
Where I am stuck: How do I make this work in cases where the customer has several child companies?
Here's the relevant part of my query as it currently stands (this is all within a IF statement that is executed when the trigger-firing action is "INSERTED")
DECLARE #insertedSupplier AS INT
DECLARE #insertedCode AS VARCHAR(50)
SET #insertedSupplier = (SELECT Supplier FROM inserted)
SET #insertedCode = (SELECT Code FROM inserted)
INSERT INTO dbo.CompanyCodes (Customer, Supplier, Code)
VALUES ((SELECT id FROM Customers WHERE Customers.ParentID = (SELECT Customer FROM inserted))
,#insertedSupplier, #insertedCode)
If there was a maximum of one possible child company, the above should work. But when there are several, "(SELECT id FROM Customers WHERE Customers.ParentID = (SELECT Customer FROM inserted)" will return several rows.
What would be simplest (and preferably best practice?) way of doing what I want to do here?
Edit with example showing desired output:
Customers table
| Customer | ID | ParentID |
|----------|----|----------|
|Customer1 | 1 | NULL |
|Customer2 | 2 | 1 |
|Customer3 | 3 | 1 |
Customer1 then proceeds to activate access to a new coupon code, and the CompanyCodes table changes to the following:
| Customer | Supplier | Code |
|----------|----------|----------|
|Customer1 | 123456 | abcdefgh |
The trigger then fires, and adds one row for each of Customer1's two child companies (where all the info, except for the Customer name, is the same as for Customer1):
| Customer | Supplier | Code |
|----------|----------|----------|
|Customer1 | 123456 | abcdefgh |
|Customer2 | 123456 | abcdefgh |
|Customer3 | 123456 | abcdefgh |
Edit 2: Also, the way our systems work, there is never more than one single insert/update/delete of a row in the CompanyCodes table (no batch jobs)

Possible to query a database into excel on a cell by cell basis? Or another solution..?

I have various large views/stored procedures that basically churns out a lot of data into an excel spread sheet. There was a problem where not all of the
company amounts weren't flowing through. I narrowed it down to a piece of code in a stored procedure: (Note this is cut down for simplicity)
LEFT OUTER JOIN view_creditrating internal_creditrating
ON creditparty.creditparty =
internalrating.company
LEFT OUTER JOIN (SELECT company, contract, SUM(amount) amount
FROM COMMON_OBJ.amount
WHERE status = 'Active'
GROUP BY company, contract) col
ON vd.contract = col.contract
Table with issue:
company | contract | amount |
| | |
TVC | NULL | 1006 |
KS | 10070 | -2345 |
NYC-G | 10060 | 334000 |
NYC-G | 100216 | 4000 |
UECR | NULL | 0 |
SP | 10090 | 84356 |
Basically some of the contracts are NULL. So when there is a LEFT OUTER JOIN on contract the null values in contract drop out and don't flow through...So i decided to do it based on company.
This also causes problems because company appears within the table more than once in order to show different contracts. With this change the query becomes ambiguous because it won't know if I want
contract 10060's amount or the contract 100216's amount and more often than not it gives me the incorrect amount. I thought about leaving the final ON clause with company = company.
This causes the least issues.... Then Somehow directly querying for for each cell value that would be inconsistent because it only affects a few cells. Although I've searched and I don't think that this is possible.
Is this possible?? OR is there another way to fix this on the database end?
As you've worked out, the problem is in the ON clause, and its use of NULL.
One way to alter the NULL to be a value you can match against is to use COALESCE, which would alter the clause to:
ON coalesce(vd.contract,'No Contract') = coalesce(col.contract,'No Contract')
This will turn all NULL's into 'No Contract', which will change the NULL=NULL test (which would return NULL) to 'No Contract'='No Contract', which will return True

Join two rows together if they share the same value?

I've shifted through views and other points and I've gotten to here. Take example below
Name | Quantity | Billed |
| | |
PC Tablet| 0 | 100 |
PC Tablet| 100 | -2345 |
Monitor | 9873 | 0 |
Keyboard | 200 | -300 |
So basically the select I would do off this view. I would want it to Bring in the data BUT it be ordered by the Name first so its in nice alphabetical order and also for a few reasons some of the records appear more then once (I think the most is 4 times). If you add the up the rows with duplicates the true 'quantity' and 'billed' would be correct.
NOTE: The actual query is very long but I broke it down for a simple example to explain the problem. The idea is the same but there is A LOT MORE columns that needs to be added together... So I'm looking for a query that would bring them together if it contains the same name. I've tried a bunch of different queries with no success either it rolls ALL the rows into one. or it won't work and I get a bunch of null errors/ name column is invalid in the select list/group by because it's not an aggregate function..
Is this even possible?
Try:
SELECT A.Name, A.TotalQty, B.TotalBilled
FROM (
SELECT Name, SUM(Quantity) as TotalQty
FROM YourTableHere
GROUP BY Name
) A
INNER JOIN
(
SELECT Name, SUM(Billed) as TotalBilled
FROM YourTableHere
GROUP BY Name
) B
ON A.Name = B.Name

Django: following the reverse direction of the one-to-one relationship

I have a question about the way Django models the one-to-one-relationship.
Suppose we have 2 models: A and B:
class B(models.Model):
bAtt = models.CharField()
class A(models.Model):
b = models.OneToOneField(B)
In the created table A, there is a field "b_id" but in the table B created by Django there is no such field as "a_id".
So, given an A object, it's certainly fast to retrieve the corresponding B object, simply through the column "b_id" of the A's row.
But how does Django retrieve the A object given the B object?
The worst case is to scan through the A table to search for the B.id in the column "b_id". If so, is it advisable that we manually introduce an extra field "a_id" in the B model and table?
Thanks in advance!
Storing the id in one of the tables is enough.
In case 1, (a.b) the SQL generated would be
SELECT * from B where b.id = a.b_id
and in case 2, the SQL would be:
SELECT * from A where a.b_id = b.id
You can see that the SQLs generated either way are similar and depend respectively on their sizes.
In almost all cases, indexing should suffice and performance would be good with just 1 id.
Django retrieves the field through a JOIN in SQL, effectively fusing the rows of the two tables together where the b_id matches B.id.
Say you have the following tables:
# Table B
| id | bAtt |
----------------
| 1 | oh, hi! |
| 2 | hello |
# Table A
| id | b_id |
-------------
| 3 | 1 |
| 4 | 2 |
Then a join on B.id = b_id will create the following tuples:
| B.id | B.bAtt | A.id |
------------------------
| 1 | oh, hi! | 3 |
| 2 | hello | 4 |
Django does this no matter which side you enter the relation from, and is thus equally effective :) How the database actually does the join depends on the database implementation, but in one way or another it has to go through every element in each table and compare the join attributes.
Other object-relational mappers require you to define relationships on both sides. The Django developers believe this is a violation of the DRY (Don't Repeat Yourself) principle, so Django only requires you to define the relationship on one end.
Always take a look at the doc ;)

DB Data migration

I have a database table called A and now i have create a new table called B and create some columns of A in table B.
Eg: Suppose following columns in tables
Table A // The one already exists
Id, Country Age Firstname, Middlename, Lastname
Table B // The new table I create
Id Firstname Middlename Lastname
Now the table A will be look like,
Table A // new table A after the modification
Id, Country, Age, Name
In this case it will map with table B..
So my problem is now i need to kind of maintain the reports which were generated before the table modifications and my friend told me you need to have a data migration..so may i know what is data migration and how its work please.
Thank you.
Update
I forgot to address the reporting issue raised by the OP (Thanks Mark Bannister). Here is a stab at how to deal with reporting.
In the beginning (before data migration) a report to generate the name, country and age of users would use the following SQL (more or less):
-- This query orders users by their Lastname
SELECT Lastname, Firstname, Age, Country FROM tableA order by Lastname;
The name related fields are no longer present in tableA post data migration. We will have to perform a join with tableB to get the information. The query now changes to:
SELECT b.Lastname, b.Firstname, a.Country, a.Age FROM tableA a, tableB b
WHERE a.name = b.id ORDER BY b.Lastname;
I don't know how exactly you generate your report but this is the essence of the changes you will have to make to get your reports working again.
Original Answer
Consider the situation when you had only one table (table A). A couple of rows in the table would look like this:
# Picture 1
# Table A
------------------------------------------------------
Id | Country | Age | Firstname | Middlename | Lastname
1 | US | 45 | John | Fuller | Doe
2 | UK | 32 | Jane | Margaret | Smith
After you add the second table (table B) the name related fields are moved from table A to table B. Table A will have a foreign key pointing to the table B corresponding to each row.
# Picture 2
# Table A
------------------------------------------------------
Id | Country | Age | Name
1 | US | 45 | 10
2 | UK | 32 | 11
# Table B
------------------------------------------------------
Id | Firstname | Middlename | Lastname
10 | John | Fuller | Doe
11 | Jane | Margaret | Smit
This is the final picture. The catch is that the data will not move from table A to table B on its own. Alas human intervention is required to accomplish this. If I were the said human I would follow the steps given below:
Create table B with columns Id, Firstname, Middlename and Lastname. You now have two tables A and B. A has all the existing data, B is empty .
Add a foreign key to table A. This FK will be called name and will reference the id field of table B.
For each row in table A create a new row in table B using the Firstname, Middlename and Lastname fields taken from table A.
After copying each row, update the name field of table A with the id of the newly created row in table B.
The database now looks like this:
# Table A
-------------------------------------------------------------
Id | Country | Age | Firstname | Middlename | Lastname | Name
1 | US | 45 | John | Fuller | Doe | 10
2 | UK | 32 | Jane | Margaret | Smith | 11
# Table B
------------------------------------------------------
Id | Firstname | Middlename | Lastname
10 | John | Fuller | Doe
11 | Jane | Margaret | Smith
Now you no longer need the Firstname, Middlename and Lastname columns in table A so you can drop them.
voilĂ , you have performed a data migration!
The process I just described above is but a specific example of a data migration. You can accomplish it in a number of ways using a number of languages/tools. The choice of mechanism will vary from case to case.
Maintenance of the existing reports will depend on the tools used to write / generate those reports. In general:
Identify the existing reports that used table A. (Possibly by searching for files that have the name of table A inside them - however, if table A has a name [eg. Username] which is commonly used elsewhere in the system, this could return a lot of false positives.)
Identify which of those reports used the columns that have been removed from table A.
Amend the existing reports to return the moved columns from table B instead of table A.
A quick way to achieve this is to create a database view that mimics the old structure of table A, and amend the affected reports to use the database view instead of table A. However, this adds an extra layer of complexity into maintaining the reports (since developers may need to maintain the database view as well as the reports) and may be deprecated or even blocked by the DBAs - consequently, I would only recommend using this approach if a lot of existing reports are affected.

Resources