How to make all rows in a table identical which the exception of 1 field? - sql

I am trying to make it so all the users have the same items because I am doing an experiment with my app and need the experimental control of flattened data.
I used the following SQL statement in my last attempt:
insert into has (email,id,qty,price,item_info,image)
select 'b#localhost.com',id,qty,price,item_info,image
from
(
select * from has
where email != 'b#localhost.com'
) as o
where o.id not in
(
select id from has
where email = 'b#localhost.com'
);
This should add all items which 'b#localhost.com' does not already have but other users do have, to 'b#localhost.com's inventory. (the 'has' table)
However, I get the following error:
The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index
I understand what this error means, but I do not understand why it is occurring. My statement inserts all records which that email doesn't already have, so there should not be any duplicate id/email pairs.
The database structure is shown below, circles are attributes, squares are tables, and diamonds are relationship sets. The HAS table is a relationship set on USER_ACCOUNT and ITEM, where the primary keys are email and id respectively.

Please try the following...
INSERT INTO has ( email,
id,
qty,
price,
item_info,
image )
SELECT a.email,
b.id,
a.qty,
a.price,
a.item_info,
a.image
FROM has a
CROSS JOIN has b
JOIN
(
SELECT email,
id
FROM has
) c ON a.email = c.email AND
b.id <> c.id;
The CROSS JOIN appends a copy of each row of has to each row of has. Please see http://www.w3resource.com/sql/joins/cross-join.php for more information on CROSS JOIN's.
Each row of the resulting dataset will have two id fields, two email fields, two qty fields, etc. By giving each instance of has an alias we create the fields a.id, b.id, a.email, etc.
We then compare each combination of "old" email address and "new" id to a list of existing email / id combinations and insert the old values with the new id replacing the old one into has
If you have any questions or comments, then please feel free to post a Comment accordingly.
Further Reading
http://www.w3resource.com/sql/joins/cross-join.php for more information on CROSS JOIN's
https://www.w3schools.com/sql/sql_in.asp for more information on WHERE's IN operator
https://www.w3schools.com/sql/sql_groupby.asp for more information on GROUP BY

I think the issue here is not that the code is trying to insert something which already exists, but that it's trying to insert more than one thing with the same PK. In lieu of an response to my comment, here is one way to get around the issue:
INSERT INTO has (email,id,qty,price,item_info,image)
SELECT
'b#localhost.com',
source.id,
source.qty,
source.price,
source.item_info,
source.image
FROM
(
SELECT email, id, qyt, price, item_info, image FROM has
) as source
JOIN
(
SELECT min(email) as min_email, id FROM has GROUP BY by id)
) as filter ON
filter.min_email = source.email
WHERE
source.id not in
(
SELECT id from has WHERE email = 'b#localhost.com'
);
The key difference from your original code is my extra join to the subquery I've aliased as filter. This limits you to inserting the has details from a single email per id. There are other ways to do the same, but I figured that this would be a safe bet for being supported by Derby.
I removed the WHERE clause from the source sub-query as that is handled by the final WHERE.

Related

Left Join on three tables in access with Group by

I have broken my head with syntax error response from Access Jet engine.
I have three tables.
First one "tblMstItem" is the master table for Item details contains two columns "colITemID" PK and "colItemName"
Second one "tblStocks" is the table where the purchases are maintained. This table has a column "colRQty" which keeps the quantity of the particular item purchased. "colItemID" FK from "tblMstItem"
Third one "tblSales" is the table where the sales are maintained. This table has a column "colSoldQty" which keeps the quantity of the particular item sold. "colItemID" FK from "tblMstItem"
Therefore "colItemID" is common in all the three tables and has links created.
My requirement is I need all the Items listed in the "tblMstItem" table columns are "colItemID" "colItemName" and if there is any item purchased or any item sold should be shown as sum of that particular item.
I have used Left Join shown in the following select statement but it always giving me an error message.
Select statement as follows:
SELECT
i.colItemID,
i.colItemName,
s.rqty,
n.soldqty
from tblMstItem i
left join
( select sum( colRQty ) as rqty from tblStocks group by colItemID ) s
on i.colItemID = s.colItemID
left join
( select sum( colSoldQty ) as soldqty from tblSales group by colItemID ) n
on i.colItemID=n.colItemID``
I tried the above given code with many different syntax but every time I get syntax error. It is making me to doubt do MS Access support three table joins, I am sure I am wrong.
See the error Message below
Table columns and table link shown below
I would be very thankful to get any help on this. Access sql please because this I am able to get results in SQL Server.
Thanks in Advance
MS Access has a picky syntax. For instance, joins need extra parentheses. So, try this:
select i.colItemID, i.colItemName,
s.rqty, n.soldqty
from (tblMstItem as i left join
(select colItemID, sum(colRQty ) as rqty
from tblStocks
group by colItemID
) as s
on i.colItemID = s.colItemID
) left join
(select colItemID, sum( colSoldQty ) as soldqty
from tblSales
group by colItemID
) as n
on i.colItemID = n.colItemID;
You also need to select colItemID in the subqueries.

Migrate data from one table into multiple tables

I have one table shoutbox_messages with user_id and text.
Now I added some new tables, chats, chat_users and chat_messages.
Now I want to insert the data from chat_messages into the new structure. I created an entry in chat, which needs to be referenced by the chat_users
INSERT INTO chat_users (user_id, chat_id)
SELECT DISTINCT shoutbox_messages.user_id,
(SELECT chats.id FROM chats WHERE chats.shoutbox = true)
FROM shoutbox_messages"
So far so good. Now I want to add all messages from shoutbox_messages to chat_messages
INSERT INTO chat_messages (chat_user_id, text, text_as_html)
SELECT chat_users.id,
(Select shoutbox_messages.text, shoutbox_messages.text_as_html
FROM shoutbox_messages Where shoutbox_messages.user_id =
(SELECT DISTINCT chat_users.id
WHERE chat_users.user_id = shoutbox_messages.user_id
)
)
from chat_users
This is not working, because shoutbox_messages have multiple texts from one user. What do I need to change, to make this work?
The problem you are having is that you are trying to insert multiple columns into one column. as well as not inserting data into the third column, which is presumably not what you want to do.
as seen here: (Select shoutbox_messages.text, shoutbox_messages.text_as_html
FROM shoutbox_messages Where shoutbox_messages.user_id = ... since your inserting data into just the text this query cannot work.
pgsql is interpreting the second query as data for just the text column
You may want to instead of doing the syntax you currently have, use sql join syntax as it would accomplish the same thing
something along the lines of
SELECT chat_users.id, shoutbox_messages.text, shoutbox_messages.text_as_html
FROM chat_users
join shoutbox_messages on shoutbox_messages.id = chat_users.id
just make sure you rewrite my code to the actual code you need to identify each column to the other

How I use where clause with computed column to get related records only

I read this FAQ http://www.firebirdfaq.org/faq289/ and I want to use select to count detail record from current record so I tried this
((
select count(*) from detail_table where detail_table.id = id
))
But I did not get correct values I got numbers like 19 where in fact it is the total number of records in detail_table! How can I get only the count of records related to current master record in master table?
The problem is that id refers to the id column of detail_table and not of the master table. So it is the equivalent of:
select count(*) from detail_table where detail_table.id = detail_table.id
Which means you are simply counting all rows. Instead - assuming the master table is master_table - you should use:
select count(*) from detail_table where detail_table.id = master_table.id
Note that, as also mentioned in the FAQ you link to, you should really consider using a view instead of a computed column when referencing other tables as it is not very good for performance.
The view equivalent would be something like
CREATE OR ALTER VIEW master_with_detail_count
AS
SELECT master_table.id, coalesce(c.detail_count, 0) as detail_count
FROM master_table
LEFT JOIN (SELECT id, count(*) as detail_count FROM detail GROUP BY id) c
ON c.id = master.id

SQL, trying to get rid of large IN cluase

I have a table,
Contacts:
Contact_ID (int)
ContactName (nvarchar)
I am given a list of contact IDs to select from. Usually, i would just do
SELECT *
FROM Contacts
WHERE IN (List of contact ID)
The problem is, the list of contact IDs could potentially get very big, like 50k or more.
So my question is, Is there a way to deal with a large list of contact IDs without having to use the IN clause?
EDIT: I'm using Microsoft sql server. The query and the contact IDs are built during runtime and passed on to sqlCommand class (c#) to be executed.
I'd create a table type with a single column and a clustered primary key.
CREATE TYPE dbo.ContactId AS TABLE
(
ContactId INT NOT NULL PRIMARY KEY
);
Pass the values into the query using a table valued parameter.
Change your query to
SELECT *
FROM Contacts
WHERE contactID IN (SELECT y.contactID FROM #yourtablevaluedparameter y)
OPTION (RECOMPILE)
The OPTION (RECOMPILE) is there to get the number of rows taken into account as the optimal plan for 50K may well be different than for 1.
You can find some example C# code for populating a TVP here
If you want performance, I would use the EXISTS clause.
SELECT c.Contact_ID, c.ContactName
FROM Contacts c
WHERE EXISTS (List of contact ID)
Create a temp table and populate your contact id in the form of rows.
Do an inner join between your table and temp table like the below.
SELECT c.*
FROM Contacts c
join #temptable t
on c.id=t.id
If you introduce index on the Join column in your temp table then your query will be more faster.

Some SQL Questions

I have been using SQL for years, but have mostly been using the query designer within SQL Studio (etc.) to put together my queries. I've recently found some time to actually "learn" what everything is doing and have set myself the following fairly simple tasks. Before I begin, I'd like to ask the SOF community their thoughts on the questions, possible answers and any tips they may have.
The questions are;
Find all records w/ a duplicate in a particular column (e.g. a linking id is in more than 1 record throughout table)
SUM price from a linked table within the same query (select within a select?)
Explain the difference between the 4 joins; LEFT, RIGHT, OUTER, INNER
Copy data from one table to another based on SELECT and WHERE criteria
Input welcomed & appreciated.
Chris
I recommend that you start by following some tutorials on this topic. Your questions are not uncommon questions for someone moving from a beginner to intermediate level in SQL. SQLZoo is an excellent resource for learning SQL so consider following that.
In response to your questions:
1) Find all records with a duplicate in a particular column
There are two steps here: find duplicate records and select those records. To find the duplicate records you should be doing something along the lines of:
select possible_duplicate_field, count(*)
from table
group by possible_duplicate_field
having count(*) > 1
What we're doing here is selecting everything from a table, then grouping it by the field we want to check for duplicates. The count function then gives me a count of the number of items within that group. The HAVING clause indicates that we want to filter AFTER the grouping to only show the groups which have more than one entry.
This is all fine in itself but it doesn't give you the actual records that have those values on them. If you knew the duplicate values then you'd write this:
select * from table where possible_duplicate_field = 'known_duplicate_value'
We can use the SELECT within a select to get a list of the matches:
select *
from table
where possible_duplicate_field in (
select possible_duplicate_field
from table
group by possible_duplicate_field
having count(*) > 1
)
2) SUM price from a linked table within the same query
This is a simple JOIN between two tables with a SUM of the two:
select sum(tableA.X + tableB.Y)
from tableA
join tableB on tableA.keyA = tableB.keyB
What you're doing here is joining two tables together where those two tables are linked by a key field. In this case, this is a natural join which operates as you would expect (i.e. get me everything from the left table which has a matching record in the right table).
3) Explain the difference between the 4 joins; LEFT, RIGHT, OUTER, INNER
Consider two tables A and B. The concept of "LEFT" and "RIGHT" in this case are slightly clearer if you read your SQL from left to right. So, when I say:
select x from A join B ...
The left table is "A" and the right table is "B". Now, when you explicitly say "LEFT" the SQL statement you are declaring which of the two tables you are joining is the primary table. What I mean by this is: Which table do I scan through first? Incidentally, if you omit the LEFT or RIGHT, then SQL implicitly uses LEFT.
For INNER and OUTER you are declaring what to do when matches don't exist in one of the tables. INNER declares that you want everything in the primary table (as declared using LEFT or RIGHT) where there is a matching record in the secondary table. Hence, if the primary table contains keys "X", "Y" and "Z", and the secondary table contains keys "X" and "Z", then an INNER will only return "X" and "Z" records from the two tables.
When OUTER is used, we're saying: Give me everything from the primary table and anything that matches from the secondary table. Hence, in the previous example, we'd get "X", "Y" and "Z" records in the output record set. However, there would be NULLs in the fields which should have come from the secondary table for key value "Y" as it doesn't exist in the secondary table.
4) Copy data from one table to another based on SELECT and WHERE criteria
This is pretty trivial and I'm surprised you've never encountered it. It's a simple nested SELECT in an INSERT statement (this may not be supported by your database - if not, try the next option):
insert into new_table select * from old_table where x = y
This assumes the tables have the same structure. If you have different structures then you'll need to specify the columns:
insert into new_table (list, of, fields)
select list, of, fields from old_table where x = y
Let's say you have 2 tables named :
[OrderLine] with the columns [Id, OrderId, ProductId, Qty, Status]
[Product] with [Id, Name, Price]
1) all orderline of command having more than 1 line (it's technically the same as looking for duplicates on OrderId :) :
select OrderId, count(*)
from OrderLine
group by OrderId
having count(*) > 1
2) total price for all order line of the order 1000
select sum(p.Price * ol.Qty) as Price
from OrderLine ol
inner join Product p on ol.ProductId = p.Id
where ol.OrderId = 1000
3) difference between joins:
a inner join b => take all a that has a match with b. if b is not found, a will be not be returned
a left join b => take all a, match them with b, include a even if b is not found
a righ join b => b left join a
a outer join b => (a left join b) union ( a right join b)
4) copy order lines to a history table :
insert into OrderLinesHistory
(CopiedOn, OrderLineId, OrderId, ProductId, Qty)
select
getDate(), Id, OrderId, ProductId, Qty
from
OrderLine
where
status = 'Closed'
To answer #4 and to perhaps show at least some understanding of SQL and the fact this isn't HW, just me trying to learn best practise;
SET NOCOUNT ON;
DECLARE #rc int
if #what = 1
BEGIN
select id from color_mapper where product = #productid and color = #colorid;
select #rc = ##rowcount
if #rc = 0
BEGIN
exec doSavingSPROC #colorid, #productid;
END
END
END