I have 3 tables in an SQL database:
Animal Status
Animals
Cart
I want to be able to delete several items from the Animals and Animal Status tables based on the cart.
What's the same between all the tables is the AnimalID which is the primary key in all the tables and based on that I'm supposed to delete them. But at the same time I need to delete it for someone with a specific email.
Example is if I have someone with the email 123#gmail.com and this person has 2 animals in his cart 1 with the ID 123 and the other with the id 456
I need to delete these 2 specific animals from the other 2 tables.
I know how to do it for 1 table but not when I have several tables that depend on each other along with them depending on the specific email when there are also other emails and animals in the cart.
Here is how it works on the outside:
x= AnimalStatus.Delete(AdoptCart.GetAnimalId(Session["email"].ToString()).ToString());
if (x > 0)
{
x = Animal.Delete(AdoptCart.GetAnimalId(Session["email"].ToString()).ToString());
if (x > 0)
{
AdoptCart.RemoveAllbyEmail(Session["email"].ToString());
Here are all the deletion codes in the order they work:
Removes the AnimalStatus based on the animals ID:
static public int Delete(string id)
{
int rowsAffected;
string strSql = string.Format("delete from AnimalStatus where AnimalID='" + id + "'");
rowsAffected = (int)dataservice.ExecuteNonQuery(strSql);
return rowsAffected;
}
Removes an animal from the animal table based on the animal's ID:
static public int Delete(string id)
{
int rowsAffected;
string strSql = string.Format("delete from Animal where AnimalID='" + id + "'");
rowsAffected = (int)dataservice.ExecuteNonQuery(strSql);
return rowsAffected;
}
Removes all animals from the Cart Table:
static public int Remove(string AnimalID)
{
int rowsAffected;
string strSql = string.Format("delete from Cart where AnimalID='" + AnimalID + "'");
rowsAffected = (int)dataservice.ExecuteNonQuery(strSql);
return rowsAffected;
}
Issue with this code is that all of that only deletes 1 animal and I need several removed.
I have tried creating a statement like this, but I didn't manage to make it work.:
Delete Animal.*, AnimalStatus.*
FROM Animal INNER JOIN
AnimalStatus ON Animal.AnimalID = AnimalStatus.AnimalID INNER JOIN
Cart ON Animal.AnimalID = Cart.AnimalID
WHERE (Cart.UserEmail = N'Email')
Based on the example I gave in words the ones that are supposed to be deleted are the following marked in //removed.
Cart Table
| AnimalID | Email_Addr |
--------------------------------------
| 123 | 123#gmail.com | //removed
| 456 | 123#gmail.com | //removed
| 765 | jj#gmail.com |
| 343 | bb#gmail.com |
| 256 | cc#gmail.com |
Animal Status Table
| AnimalID | Vaccinated |
--------------------------------------
| 123 | Yes | //removed
| 456 | Yes | //removed
| 765 | No |
| 343 | No |
| 256 | No |
Animals Tables
| AnimalID | Age |
--------------------------------------
| 123 | 3 | //removed
| 456 | 4 | //removed
| 765 | 3 |
| 343 | 7 |
| 256 | 10|
Looking at your current work there are a few things you could do. You could rewrite your query logic like you're asking, but what I'm not seeing in your example is primary/foreign key relationships. So cascade won't work with how you currently have your tables set up. This is a good write up of cascade delete. Cascade Delete
So if you don't want to rewrite everything you really could just do all of this in your C# code. All you'd have to do is loop through the cart table and get all of the id's that have the e-mail "123#gmail.com" and call your three delete statements. You'd just need to write a method to grab the id's and call the delete methods until the id variable is empty.
It just depends where you want all of the logic to exist, in sql or in your C# code. If you're using Entity Framework, whether code first or database first, you have options to use LINQ Query syntax or extension methods. Just depends on preference really.
Related
I have the following 3 table definitions in my SQL Server database:
+----------------------------------+-----------------+------------------------------------------------
| Product | Rating | Image |
+----------------------------------+-----------------+-------------------------------------------------
| ProductId | Id | Id |
| ProdctName | Rating | Image |
| | ProductId FK References(Product)| ProductId FK References(Product)|
+----------------------------------+-----------------+---------------+----------------------------------
And these tables contain the following sample data:
+----------------------------------+------------
|Product | ProductId |ProductName |
+----------------------------------+------------
| | 1 |Prodcuct 1 |
| | 2 |Prodcuct 2 |
| | 3 |Prodcuct 3 |
| | 4 |Prodcuct 4 |
+----------------------------------+------------+
+----------------------------------+----------------------+
|Rating | Id |RatingVal |ProductId |
|+----------------------------------+-----------------------
| | 1 |3 |1 |
| | 2 |2 |2 |
| | 3 |3 |2 |
| | 4 |5 |3 |
| | 5 |4 |3 |
+---------------------+------------+------------+----------+
+----------------------------------+----------------------+
|Image | Id |ImagePath |ProductId
+----------------------------------+-----------------------
| | 1 |ABC |1 |
| | 2 |XYZ |2 |
| | 3 |LMN |3 |
| | 4 |PQR |4 |
+---------------------+------------+------------+----------+
I need to gather information about a product in one place, such that each product contains the details about product ( from products table), related average rating ( from ratings table)m and the image path for the product ( from Image table). In other words I need the following output:
+----------------------------------+--------------------------------+
|Output | ProductId |ProductName |Avg(Rating)|ImgPath|
+----------------------------------+--------------------------------+
| | 1 |Prodcuct 1 |3 |ABC |
| | 2 |Prodcuct 2 |2.5 |XYZ |
| | 3 |Prodcuct 3 |4.5 |LMN |
| | 4 |Prodcuct 4 |0.0 |PQR |
+----------------------------------+------------+-----------+-------+
I am using Entity Framework to fetch this data, and entities in context class in my code( shown below).
My question is: How do I produce my desired output for all the products.
My code below is not able to get all the data I want. The problem is that the product with id4 is not shown in the result, I assume this is be cause product 4 does not have an entry in the ratings table.
var trendingProducts = (from ratings in entities.Rating
group ratings by new { ratings.productId } into c
join products in entities.Products on c.FirstOrDefault().productId equals products.ProductID
join images in entities.Images on c.Key.productId equals images.ProductId
select new ProductViewModel
{
ProductId = products.ProductId,
ProductName = products.ProductName,
RatingVal = c.Average(l => l.RatingVal) == null ? 0 : c.Average(l => l.Rating),
ImagePath = images.ImagePath,
}).ToList();
So you have a table of Products, a table of Ratings and a table of Images.
Every Product has zero or more Ratings, every Rating belongs to exactly one Product using foreign key ProductId. Similarly, every Product has zero or more Images, every Image belongs to exactly one Image, using foreign key ProductId. Just standard one-to-many relations.
It might be that every Product has zero-or-one Image: in that case you have a zero-or-one-to-one relation. The code will be similar. The main difference is that 'Product' won't have a collection of Images, it will only have one virtual Image.
If you follow the entity framework code first conventions you'll have classes like:
class Product
{
public int Id {get; set;}
// every Product has zero or more Ratings:
public virtual ICollection<Rating> Ratings {get; set;}
// every product has zero or more Images:
public virtual ICollection<Image> Images {get; set;}
... // other properties
}
class Rating
{
public int Id {get; set;}
// every Rating belongs to exactly one Product using foreign key:
public int ProductId {get; set;}
public virtual Product Product {get; set;}
...
}
class Image
{
public int Id {get; set;}
// every Image belongs to exactly one Product using foreign key:
public int ProductId {get; set;}
public virtual Product Product {get; set;}
...
}
This is everything entity framework needs to know to detect your one-to-many relations. It knows which tables / columns you want, and it knows the relations between the tables. It might be that you want different identifiers for your tables or properties. In that case you'll need attributes or fluent API. But that is out of scope of this question.
Note that in entity framework all non-virtual properties will become columns in your tables. All virtual properties represent the relations between the tables.
I need to gather information about a product in one place, such that
each product contains the details about product ( from products
table), related average rating ( from ratings table) and the image
path for the product (from Image table).
Whenever people query for "objects with their sub-object" they tend to create a (group)join. However, if you use entity framework it is much easier to use the ICollections for these queries. If you use an ICollection, Entity Framework will know that a (group)join is needed.
var result = myDbContext.Products // from the collection of Products
.Where(product => ...) // select only those Products that ...
.Select(product => new // from every remaining Product make one new
{ // object with the following properties:
// Select only the properties you actually plan to use!
Id = product.Id,
Name = product.ProductName,
...
AverageRating = product.Ratings // from all Ratings of this Product
.Select(rating => rating.Rating) // get the Rating value
.Average(), // and Average it.
Images = product.Images // from all Images of this product
.Select(image => image.ImagePath) // select the ImagePath
.ToList(),
// or if a Product has only zero or one Image:
Image = product.Image?.ImagePath // if the product has an Image, get its ImagePath
// or use null if there is no Image
});
The nice thing about using the ICollections is that the code is simplere and more natural, it looks more similar to the text of your requirement than a (group)join. Besides, if more than two tables are involved (group)joins tend to look horrible.
I won't try to give a solution using a GroupJoin. I'm sure other answers will show this. Compare and see for yourself which solution seems easier to understand.
I was wondering if you can help me. Firstly I wanted to say that this is my first application in C# that I am created so please accept that my code is not perfect.
What I want to achieve:
My application will be a Car Repair Management App. I have a panel on which I have few texboxes and a list. With a single button click I want to make it so that data in text boxes will be stored in table called 'naprawa' and data from list to be stored in table called 'opisynapraw'. These tables are related with FK from 'naprawa' so I want that items from list when they are stored they will have FK of just created field in other table. (If that makes sense)
Please see table setup below:
Naprawa
|--------------+----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
|--------------+----------------+------+-----+---------+----------------+
| Nr_Naprawy | int(11) | NO | PRI | NULL | auto_increment |
| data_naprawy | date | YES | | NULL | |
| nr_rej | varchar(45) | YES | MUL | NULL | |
| Przebieg | int(15) | YES | | NULL | |
|--------------+----------------+------+-----+---------+----------------+
Data in that table:
|------------+--------------+---------+-----------|
| Nr_Naprawy | data_naprawy | nr_rej | Przebieg |
|------------+--------------+---------+-----------|
| 1 | 2018-06-20 | na06ysa | 150000 |
|------------+--------------+---------+-----------|
Table opisynapraw:
+---------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+---------------+------+-----+---------+----------------+
| idopisynapraw | int(11) | NO | PRI | NULL | auto_increment |
| Opis_Naprawy | varchar(45) | YES | | NULL | |
| Cena | decimal(10,2) | YES | | NULL | |
| Nr_Naprawy | int(11) | YES | MUL | NULL | |
+---------------+---------------+------+-----+---------+----------------+
Example Data that I would like to see in that table:
|---------------+--------------+-------+-------------|
| idopisynapraw | Opis_Naprawy | Cena | Nr_Naprawy |
|---------------+--------------+-------+-------------|
| 1 | notes abcd | 30 | 1 |
|---------------+--------------+-------+-------------|
| 2 | notes cdef | 5 | 1 |
|---------------+--------------+-------+-------------|
What I want my code to do. I when I press Add button it will add the record from text boxes into 'naprawa'. Then take the ID of what was added and use it as FK for opisynapraw and add it along with data from listveiw to opisynapraw table.
Here is my code.
private void btnDodajNaprawe_Click(object sender, EventArgs e)
{
try
{
MySqlConnection myConn3 = new MySqlConnection(MyConnection);
myConn3.Open();
string querydoajnap = "INSERT INTO naprawa (data_naprawy,nr_rej,Przebieg) VALUES('" + dtaData.Value.Date.ToString("yyyy/MM/dd") + "', '" + txtNrRej.Text + "', '" + txtPrzebieg.Text + "');";
MySqlCommand cmd = new MySqlCommand(querydoajnap, myConn3);
MySqlConnection lastidconn = new MySqlConnection(MyConnection);
lastidconn.Open();
if (cmd.ExecuteNonQuery() == 1)
{
MessageBox.Show("Dodane");
txtNrRej.Text = string.Empty;
txtPrzebieg.Text = string.Empty;
}
else
{
MessageBox.Show("Blad");
}
String LastIDnapr = "select LAST_INSERT_ID();";
MySqlCommand cmd1 = new MySqlCommand(LastIDnapr, lastidconn);
MySqlDataReader IDRead = cmd1.ExecuteReader();
int idnumber = 0;
while (IDRead.Read())
{
idnumber = IDRead.GetInt32(0);
}
MySqlCommand cmd2 = myConn3.CreateCommand();
foreach (ListViewItem item in listView1.Items)
{
// opisynapraw(Opis_Naprawy,Cena,Nr_Naprawy) VALUES (#val1 , #val2, '" + idnumber + "');";
cmd2.Parameters.AddWithValue("#val1",item.Text);
cmd2.Parameters.AddWithValue("#val2",item.SubItems[1].Text);
cmd2.CommandText = "INSERT INTO opisynapraw(Opis_Naprawy,Cena,Nr_Naprawy) VALUES (#val1 , #val2, '" + idnumber + "');";
cmd2.ExecuteNonQuery();
}
lastidconn.Close();
myConn3.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
When I execute the above code it works for adding data to table 'Naprawa' but I think it will not take last used ID to relate to 'opisynapraw' table what brings following error.
For some reason I need some rep points to post images... I will type below what error message I'm having
Cannot add or update a child row; a foreign key contraint fails
('cars','opisynapraw', CONSTRAINT 'Nr_Naprawy'FOREIGN Key
('Nr_Naprawy') REFERENCES 'naprawa'('Nr_Naprawy') ON DELETE NO ACTION
ON UPDATE NO ACTION)
I would very appreciate any help in resolving this mystery.
Thank You in advance
Rogupl
You need the INSERT into Naprawa to give you back the auto generated value in the same query. Add this into the end of your insert statement, after the ;
SELECT LAST_INSERT_ID();
Then to get it back, change the ExecuteNonQuery into ExecuteScalar - the value it returns will be the value you need - it returns it as an object, so either cast it or System.Convert it. (This requires you to remove your check that it inserted 1 row; but bear in mind it will throw an exception of it fails.)
Now remove the code relating to the LastIDnapr query, it should work fine.
Additional tips:
Your code is vulnerable to SQL injection attacks: use SQL parameters rather than string concatenation to construct a query.
MySqlConnection, MySqlCommand, and MySqlDataReader are all IDisposable so each should be in a using block.
Those connections are connecting to the same database so ensure that they don't overlap. This will be obvious once you're using using blocks.
Take a look at can we stop using AddWithValue
You should always use SqlCommand and it's Parameters due to dangerous of sql injection code.
Also, a good idea is to make a database handler class, which will handle all these things.
But let's come back to your problem, I strongly believe you're trying to insert id to table opisynapraw, that doesn't exist in table naprawa as id. Please check what id you're getting, what you're trying to insert and verify it with database data.
If I got this list
PersonPhone
------------------------------------------------------------
| **PersonId** |**Name** | **PhoneId** | **PhoneNumber** |
------------------------------------------------------------
| 1 | John Doe | 1 | 111-55-5855 |
------------------------------------------------------------
| 1 | John Doe | 2 | 111-55-5521 |
------------------------------------------------------------
| 2 | Mary Jane| 3 | 254-565-855 |
------------------------------------------------------------
| 3 | J. Watson| NULL| NULL|
------------------------------------------------------------
I need to mapping to this object:
public class PersonContactInfo {
public int Id { get; set; }
public string Name { get; set; }
public List<string> Phones { get; set; }
}
With LINQ, how can I get one row for each person, with his phones in a List and paging?
I already have a query which result is like the PersonPhone result set, but I don't know how to grouping by PersonId and then join all the PhoneNumbers to List, and paging. For example, if I want a page size of three, how to make a sql query to get John Doe, Mary Jane and J. Watson with their phones, if the actual query returns 4 rows?
Note: I'm not (and can't) using Entity Framework, what I'm doing is and sql query that populate a list of PersonPhone, just like EF does.
Applying a group by:
var query= PersonPhoneSet
.GroupBy(p=>new {p.PersonId, p.Name})
.Select(g=> new PersonContactInfo
{
Id=g.Key.PersonId,
Name=g.Key.Name,
Phones= g.Select(p=>p.PhoneNumber).ToList()
}
);
I have a table of items using linq to entities, say:
ID | Name | Attb1 | Attb2
1 | Apple | Green | Juicy
2 | Orange | Orange | sweet
etc
I have another list with just ID's in it.
Using Linq I want to return all fields from the item table except where the ID is in the list. i.e. if my list just has '1' in it I want to return 2 | orange | orange | sweet
You could try something like this
var result = table.Where(x => !list.Contains(x.id));
where I have assumed that table holds all the rows of your table and list contains the ids that you want to exclude.
I have denormalized data:
+----+----------+------+--------+
| pk | name | type | animal |
+----+----------+------+--------+
| 1 | alex | car | cat |
| 2 | alex | bike | cat |
| 3 | liza | car | dog |
| 4 | danielle | bike | dog |
| 5 | danielle | bus | dog |
+----+----------+------+--------+
I would like to have 3 dropdown boxes.
name
type
animal
after the user chooses the option for the first, there should be a cascade effect for the other dropdowns.
example: if the user chooses danielle for the name, the only two options for type would be bike and bus and the only option for animal would be dog
How should I structure the SQL tables? Should I denormalize?
I'd say that your solution depends on how much data do you have in this table.
If this table is relatively small, you could load it into memory, fill comboboxes by distinct values and then filter data by chosen field.
If it large, you maybe should denormalize your table as #astander says, fill comboboxes with data from reference tables and then when value changes, select filters from SQL like:
declare #name_id int -- input parameter, fill it with id of chosen name
-- filter for type combo
select distinct type_id from main_table where name_id = #name_id
-- filter for animal combo
select distinct animal_id from main_table where name_id = #name_id