Regarding English Query - Request help - sql-server-2000

I work as a researcher at Tata Research Development and Design Centre, India . I am exploring the available Natural Language Interfaces for database.While exploring MS English Query, I found that certain type of relationship does not give appropriate answer.
Our schema looks like as shown below:
Schema:
Customer ( customer_id , customer_name, customer_address)
Transaction ( transaction_id , customer_id_1, customer_id_2, amount)
CUSTOMER
cuctomer_id customer_name customer_address
1 John abc
2 Rohit pqr
3 Mark xyz
TRANSACTION
transaction_id customer_id_1 customer_id_2 amount
1 1 2 200
2 1 3 300
3 2 1 300
Here, customer_id_1 and customer_id_2 references to Customer.customer_id.
More than two attributes of a table refers to same primary key attribute in another table.
Query : “Give me all transactions between John and Rohit”
The entities we made were Customer and transaction; for the given English query,following relationship was made between customers and transactions-
Relationship Type : Noun Verb
Relationship: Transaction are
Preposition Clause: between Customer(customer_id_1)
Preposition Clause: and Customer_1(customer_id_2)
Here we defined two different roles of customers(through 2 join path-customer_id_1 and customer_id_2)
The output we expect was transactions with trasaction_id as 1 and 3, but it was joining on customer_id_1 only and gave result as
Expected Output-
transaction_id customer_id_1 customer_id_2 amount
1 1 2 200
3 2 1 300
Actual Output-
transaction_id customer_id_1 customer_id_2 amount
1 1 2 200
2 1 3 300
This is happening because of ambiguity between default and and and we defined in preposition clause..
We also tried to create another relationship
1)
Relationship Type : Noun Verb
Relationship: Transaction are
Preposition Clause: by Customer(customer_id_1)
Preposition Clause: to Customer_1(customer_id_2)
2)
Relationship Type : Noun Verb
Relationship: Transaction are
Preposition Clause: by Customer(customer_id_2)
Preposition Clause: to Customer_1(customer_id_1)
The output we expect was transactions with trasaction_id as 1 and 3, here It took join on both customer_id_1 andcustomer_id_2,
But only relationship 1 was used and not both 1 and 2, so output was as follows
Expected Output-
transaction_id customer_id_1 customer_id_2 amount
1 1 2 200
3 2 1 300
Actual Output-
transaction_id customer_id_1 customer_id_2 amount
1 1 2 200
Is there a work around for creating relationships that would make possible the above query to be understood by MSEQ?
Hope someone help us by suggesting some appropriate solution.

I couldn't understand what you wrote in length. It would make sense if you could provide what exact query you had written. Anyway, try the following query, it will work.
SELECT [Transaction_Id]
,[Customer_Id_1]
,[Customer_Id_2]
,[Amount]
FROM [TEST].[dbo].[Transaction] WHERE ((Customer_Id_1 = 1 AND Customer_Id_2 = 2)OR(Customer_Id_1 = 2 AND Customer_Id_2 = 1))

Related

Case statement logic and substring

Say I have the following data:
Passes
ID | Pass_code
-----------------
100 | 2xBronze
101 | 1xGold
102 | 1xSilver
103 | 2xSteel
Passengers
ID | Passengers
-----------------
100 | 2
101 | 5
102 | 1
103 | 3
I want to count then create a ticket in the output of:
ID 100 | 2 pass (bronze)
ID 101 | 5 pass (because it is gold, we count all passengers)
ID 102 | 1 pass (silver)
ID 103 | 2 pass (steel)
I was thinking something like the code below however, I am unsure how to finish my case statement. I want to substring pass_code so that we get show pass numbers e.g '2xBronze' should give me 2. Then for ID 103, we have 2 passes and 3 customers so we should output 2.
Also, is there a way to firstly find '2xbronze' if the pass_code contained lots of other things such as '101001, 1xbronze, FirstClass' - this may change so i don't want to substring, could we search for '2xbronze' and then pull out the 2??
SELECT
CASE
WHEN Passes.pass_code like '%gold%' THEN Passengers.passengers
WHEN Passes.pass_code like '%steel%' THEN SUBSTRING(passes.pass_code, 1,1)
WHEN Passes.pass_code like '%bronze%' THEN SUBSTRING(passes.pass_code, 1,1)
WHEN Passes.pass_code like '%silver%' THEN SUBSTRING(passes.pass_code, 1,1)
else 0 end as no,
Passes.ID,
Passes.Pass_code,
Passengers.Passengers
FROM Passes
JOIN Passengers ON Passes.ID = Passengers.ID
https://dbfiddle.uk/?rdbms=oracle_18&fiddle=db698e8562546ae7658270e0ec26ca54
So assuming you are indeed using Oracle (as your DB fiddle implies).
You can do some string magic with finding position of a splitter character (in your case the x), then substringing based on that. Obviously this has it's problems, and x is a bad character seperator as well.. but based on your current set.
WITH PASSCODESPLIT AS
(
SELECT PASSES.ID,
TO_Number(SUBSTR(PASSES.PASS_CODE, 0, (INSTR(PASSES.PASS_CODE, 'x')) - 1)) AS NrOfPasses,
SUBSTR(PASSES.PASS_CODE, (INSTR(PASSES.PASS_CODE, 'x')) + 1) AS PassType
FROM Passes
)
SELECT
PASSCODESPLIT.ID,
CASE
WHEN PASSCODESPLIT.PassType = 'gold' THEN Passengers.Passengers
ELSE PASSCODESPLIT.NrOfPasses
END AS NrOfPasses,
PASSCODESPLIT.PassType,
Passengers.Passengers
FROM PASSCODESPLIT
INNER JOIN Passengers ON PASSCODESPLIT.ID = Passengers.ID
ORDER BY PASSCODESPLIT.ID ASC
Gives the result of:
ID NROFPASSES PASSTYPE PASSENGERS
100 2 bronze 2
101 5 gold 5
102 1 silver 1
103 2 steel 3
As can also be seen in this fiddle
But I would strongly advise you to fix your table design. Having multiple attributes in the same column leads to troubles like these. And the more variables/variations you start storing, the more 'magic' you need to keep doing.
In this particular example i see no reason why you don't simply have the 3 columns in Passes, also giving you the opportunity to add new columns going forward. I.e. to keep track of First class.
You can extract the numbers using regexp_substr(). So I think this does what you want:
SELECT (CASE WHEN p.pass_code LIKE '%gold%'
THEN TO_NUMBER(REGEXP_SUBSTR(p.pass_code, '^[0-9]+'))
ELSE pp.passengers
END) as num,
p.ID, p.Pass_code, pp.Passengers
FROM Passes p JOIN
Passengers pp
ON p.ID = pp.ID;
Here is a db<>fiddle.
This converts the leading digits in the code to a number. Also note the use of table aliases to simplify the query.

How to create two JOIN-tables so that I can compare attributes within?

I take a Database course in which we have listings of AirBnBs and need to be able to do some SQL queries in the Relationship-Model we made from the data, but I struggle with one in particular :
I have two tables that we are interested in, Billing and Amenities. The first one have the id and price of listings, the second have id and wifi (let's say, to simplify, that it equals 1 if there is Wifi, 0 otherwise). Both have other attributes that we don't really care about here.
So the query is, "What is the difference in the average price of listings with and without Wifi ?"
My idea was to build to JOIN-tables, one with listings that have wifi, the other without, and compare them easily :
SELECT avg(B.price - A.price) as averagePrice
FROM (
SELECT Billing.price, Billing.id
FROM Billing
INNER JOIN Amenities
ON Billing.id = Amenities.id
WHERE Amenities.wifi = 0
) A, (
SELECT Billing.price, Billing.id
FROM Billing
INNER JOIN Amenities
ON Billing.id = Amenities.id
WHERE Amenities.wifi = 1) B
WHERE A.id = B.id;
Obviously this doesn't work... I am pretty sure that there is a far easier solution to it tho, what do I miss ?
(And by the way, is there a way to compute the absolute between the difference of price ?)
I hope that I was clear enough, thank you for your time !
Edit : As mentionned in the comments, forgot to say that, but both tables have idas their primary key, so that there is one row per listing.
Just use conditional aggregation:
SELECT AVG(CASE WHEN a.wifi = 0 THEN b.price END) as avg_no_wifi,
AVG(CASE WHEN a.wifi = 1 THEN b.price END) as avg_wifi
FROM Billing b JOIN
Amenities a
ON b.id = a.id
WHERE a.wifi IN (0, 1);
You can use a - if you want the difference instead of the specific values.
Let's assume we're working with data like the following (problems with your data model are noted below):
Billing
+------------+---------+
| listing_id | price |
+------------+---------+
| 1 | 1500.00 |
| 2 | 1700.00 |
| 3 | 1800.00 |
| 4 | 1900.00 |
+------------+---------+
Amenities
+------------+------+
| listing_id | wifi |
+------------+------+
| 1 | 1 |
| 2 | 1 |
| 3 | 0 |
+------------+------+
Notice that I changed "id" to "listing_id" to make it clear what it was (using "id" as an attribute name is problematic anyways). Also, note that one listing doesn't have an entry in the Amenities table. Depending on your data, that may or may not be a concern (again, refer to the bottom for a discussion of your data model).
Based on this data, your averages should be as follows:
Listings with wifi average $1600 (Listings 1 and 2)
Listings without wifi (just 3) average 1800).
So the difference would be $200.
To achieve this result in SQL, it may be helpful to first get the average cost per amenity (whether wifi is offered). This would be obtained with the following query:
SELECT
Amenities.wifi AS has_wifi,
AVG(Billing.price) AS avg_cost
FROM Billing
INNER JOIN Amenities ON
Amenities.listing_id = Billing.listing_id
GROUP BY Amenities.wifi
which gives you the following results:
+----------+-----------------------+
| has_wifi | avg_cost |
+----------+-----------------------+
| 0 | 1800.0000000000000000 |
| 1 | 1600.0000000000000000 |
+----------+-----------------------+
So far so good. So now we need to calculate the difference between these 2 rows. There are a number of different ways to do this, but one is to use a CASE expression to make one of the values negative, and then simply take the SUM of the result (note that I'm using a CTE, but you can also use a sub-query):
WITH
avg_by_wifi(has_wifi, avg_cost) AS
(
SELECT Amenities.wifi, AVG(Billing.price)
FROM Billing
INNER JOIN Amenities ON
Amenities.listing_id = Billing.listing_id
GROUP BY Amenities.wifi
)
SELECT
ABS(SUM
(
CASE
WHEN has_wifi = 1 THEN avg_cost
ELSE -1 * avg_cost
END
))
FROM avg_by_wifi
which gives us the expected value of 200.
Now regarding your data model:
If both your Billing and Amenities table only have 1 row for each listing, it makes sense to combine them into 1 table. For example: Listings(listing_id, price, wifi)
However, this is still problematic, because you probably have a bunch of other amenities you want to model (pool, sauna, etc.) So you might want to model a many-to-many relationship between listings and amenities using an intermediate table:
Listings(listing_id, price)
Amenities(amenity_id, amenity_name)
ListingsAmenities(listing_id, amenity_id)
This way, you could list multiple amenities for a given listing without having to add additional columns. It also becomes easy to store additional information about an amenity: What's the wifi password? How deep is the pool? etc.
Of course, using this model makes your original query (difference in average cost of listings by wifi) a bit tricker, but definitely still doable.

Table Join issue

Right now I've got a Main table in which I am uploading data. Because the Main table has many different duplicates, I Append various data out of the Main table into other tables such as, username, phone number, and locations in order to keep things optimized. Once I have everything stripped down from the Main table, I then append what's left into a final optimized Main table. Before this happens though, I run a select query joining all the stripped tables with the original Main table in order to connect the IDs from each table, with the correct data. For example:
Original Main Table
--Name---------Number------Due Date-------Location-------Charges Monthly-----Charges Total--
John Smith 111-1111 4/3 Chicago 234.56 500.23
Todd Jones 222-2222 4/3 New York 174.34 323.56
John Smith 111-1111 4/3 Chicago 274.56 670.23
Bill James 333-3333 4/3 Orlando 100.00 100.00
This gets split into 3 tables (name, number, location) and then there is a date table with all the dates for the year:
Name Table Number Table Location Table Due Date Table
--ID---Name------ -ID--Number--------- ---ID---Location---- --Date---
1 John Smith 1 111-1111 1 Chicago 4/1
2 Todd Jones 2 222-2222 2 New York 4/2
3 Bill James 3 333-3333 3 Orlando 4/3
Before The Original table gets stripped, I run a select query that grabs the ID from the 3 new tables, and joins them based on the connection they have with the original Main table.
Select Output
--Name ID----Number ID---Location ID---Due Date--
1 1 1 4/3
2 2 2 4/3
1 1 1 4/3
3 3 3 4/3
My issue comes when I need to introduce a new table that isn't able to be tied into the Original Main Table. I have an inventory table that, much like the original Main table, has duplicates and needs to be optimized. I do this by creating a secondary table that takes all the duplicated devices out and put them in their own table, and then strips the username and number out and puts them into their tables. I would like to add the IDs from this new device table into the select output that I have above. Resulting in:
Select Output
--Name ID----Number ID---Location ID---Due Date--Device ID---
1 1 1 4/3 1
2 2 2 4/3 1
1 1 1 4/3 2
3 3 3 4/3 1
Unlike the previous tables, the device table has no relationship to the originalMain Table, which is what is causing me so much headache. I can't seem to find a way to make this happen...is there anyway to accomplish this?
Any two tables can be joined. A table represents an application relationship. In some versions (not the original) of Entity-Relationship Modelling (notice that the "R" in E-R stands for "(application) relationship"!) a foreign key is sometimes called a "relationship". You do not need other tables or FKs to join any two tables.
Explain, in terms of its column names and the values for those names, exactly when a row should turn up in the result. Maybe you want:
SELECT *
FROM the stripped-and-ID'd version of the Original AS o
JOIN the stripped-and-ID'd version of the Device AS d
USING NameID, NumberID, LocationID and DueDate
Ie
SELECT *
FROM the stripped-and-ID'd version of the Original AS o
JOIN the stripped-and-ID'd version of the Device AS d
ON o.NameID=d.NameId AND o.NumberID=d.NumberID
AND o.LocationID=d.LocationID AND o.DueDateID=d.DueDate.
Suppose p(a,...) is some statement parameterized by a,... .
If o holds the rows where o(NameID,NumberID,LocationID,DueDate) and d holds the rows where d(NameID,NumberID,LocationID,DueDate,DeviceID) then the above holds the rows where o(NameID, NumberID, LocationID, DueDate) AND d(NameID,NumberID,LocationID,DueDate,DeviceID). But you really have not explained what rows you want.
The only way to "join" tables that have no relation is by unioning them together:
select attribute1, attribute2, ... , attributeN
from table1
where <predicate>
union // or union all
select attribute1, attribute2, ... , attributeN
from table2
where <predicate>
the where clauses are obviously optional
EDIT
optionally you could join the tables together by stating ON true which will act like a cross product

Cascading query SQL SERVER

I have 3 SQL SERVER data tables :
TBL_HOUSE :
|"ID_HOUSE"|"ID_PERSONS"|"QTY_PERSONS"|
|"1" |"|1|2|3|" |"|1|2|1|" |
|"2" |"|2|" |"|3|" |
TBL_PERSON :
|"ID_PERSON"|"ID_PETS"|"QTY_PETS"|
|"1" |"|3|1|" |"|1|2|" |
|"2" |"|1|2|" |"|3|1|" |
TBL_PET :
|"ID_PET"|"PET_TYPE"|"PET_PRICE"|
|"1" |"DOG" |"500" |
|"2" |"CAT" |"200" |
I have to make two queries.
The first for retrieve the number of each PET in a house.
ie. : In the HOUSE"2", there is 3 PERSON"2"
for each PERSON"2" there is 3 DOG and 1 CAT
In total in the HOUSE"2" is 9 DOG and 3 CAT.
The second to get the total value of pets in a house.
In HOUSE"2", the total value is 5100. (3*(3*500+1*200) = 5100)
Can you help me to write these queries?
Thanks a lot.
Sorry, it's all I can do for you. Your DataBase design makes me feel sick.
This query return you TBL_Person table in a NORMAL form. I hope you are smart enough to do the same with another tables and count all amounts that you need.
WITH Split(id_person, stpos_pets, stpos_qpets,endpos_pets, endpos_qpets)
AS(
SELECT id_person, 0 AS stpos_pets, 0 AS stpos_qpets, CHARINDEX('|',id_pets) AS endpos_pets, CHARINDEX('|',qty_pets) AS endpos_qpets
from TBL_PERSON
UNION ALL
SELECT TBL_PERSON.id_person, endpos_pets+1,endpos_qpets+1, CHARINDEX('|',id_pets,endpos_pets+1),CHARINDEX('|',qty_pets,endpos_qpets+1)
FROM TBL_PERSON,Split
WHERE endpos_pets > 0 AND #t.id_person=split.id_person
)
SELECT 'Id' = TBL_PERSON.id_person,
'Pets' = SUBSTRING(id_pets,stpos_pets,COALESCE(NULLIF(endpos_pets,0),LEN('|3|1|')+1)-stpos_pets),
'Quantity' = SUBSTRING(qty_pets,stpos_qpets,COALESCE(NULLIF(endpos_qpets,0),LEN('|3|1|')+1)-stpos_qpets)
FROM Split,TBL_PERSON
Where stpos_pets>0 and endpos_pets>0 and stpos_qpets>0 and endpos_qpets>0 and TBL_PERSON.id_person=split.id_person

How to update a column when the same column in another table is changed ?(postgresql)

I need A Function or a Trigger to solve this Problem??
customer_details :::
custid name creditid
----------------------------
2 a 1
3 b 2
4 c 3
balance_amount :::
creditid credit_type balance
-----------------------------------
1 rent 1000
1 transport 2000
1 food 1000
1 fruits 1500
2 rent 1500
2 transport 1020
2 food 1200
2 fruits 1000
3 transport 1600
3 rent 2000
3 food 1540
3 fruits 1560
Pay_the_loan :::
creditid credit_type Pay status
---------------------------------------------
1 rent 500 null
2 fruits 600 null
3 transport 400 null
1 fruits 500 null
once i update the status column in pay_the_loan table to ok for a particular creditid i.e..,
(update pay_the_loan set status='ok' where creditid=2)
then it should deduct the amount from the balance column in balance_amount table and it should be updated i.e..,(1000-600=400 in balance_amount table where balance_amount.credit_type=fruits and creditid=2 from balance amount table)
Possible post me a Function or a Trigger to solve this problem ?
You're probably better off restructuring a little, creating a loan_amount table with the original loans, and just make balance_amount a view that shows the current balance with all payments deducted. That way, for example, a correction in a payment amount will not make your system show the wrong balance.
A view that calculates the current balance for you could look something like;
CREATE VIEW balance_amount AS
SELECT la.creditid, la.credit_type, amount
- COALESCE(SUM(pay),0) balance
FROM loan_amount la
LEFT JOIN pay_the_loan pl
ON la.creditid = pl.creditid
AND la.credit_type = pl.credit_type
AND pl.status = 'OK'
GROUP BY la.creditid, la.credit_type, la.amount;
An SQLfiddle with the whole setup.
You can update both tables in the same query:
update
pay_the_loan
set
pay_the_loan.status='ok',
balance_amount.balance=balance_amount.balance-pay_the_loan.Pay
from balance_amount
where
pay_the_loan.creditid=balance_amount.creditid
and
pay_the_loan.creditid=2
and
balance_amount.credit_type='fruits';
Update. I've read the documentation of postgresql update statement. Apparently, I was wrong and it is not possible to update two tables in a single query. However, I still think that you do not need a trigger here. Just use two update queries one after another:
update
pay_the_loan
set
status='ok'
where
pay_the_loan.creditid=2;
update
balance_amount
set
amount=balance_amount.amount-pay_the_loan.Pay
from pay_the_loan
where
pay_the_loan.creditid=balance_amount.creditid
and
pay_the_loan.creditid=2
and
balance_amount.credit_type='fruits';