sql server update many columns with a select from a join of a table variable and a db table - tsql

I have the classical person -> person attributes scheme.
So, like this: person(PK) <- person_attribute(FK)
What I need is a query to get one row where a person is joined with her attributes.
For example, to transform:
Person:
{ ID = 123456, Name = 'John Smith', Age = 25 }
Attributes:
1) { PersonID = 123456, AttributeTypeID = 'Height', AttributeValue = '6'6'''}
2) { PersonID = 123456, AttributeTypeID = 'Weight', AttributeValue = '220lbs'}
3) { PersonID = 123456, AttributeTypeID = 'EyeColor', AttributeValue = 'Blue'}
To:
PersonWithAttributes
{
ID = 123456, Name = 'John Smith', Age = 25, Height = '6'6''', Weight = '220lbs', EyeColor = 'Blue'
}
To make things worse my persons are in a table variable.
So, I have (in a sp with a parameter of person_id):
--result table
declare #people_info table
(
person_id int,
name nvarchar(max),
age int,
height nvarchar(10) null,
weight nvarchar(10) null,
eye_color nvarchar(16) null
)
insert into #people_info
select person_id, name, age, null, null, null
from dbo.HR.people where person_id = #person_id
update pi
set
pi.height = (select pa.attribute_value where pa.attribute_type_id = 'Height'),
pi.height = (select pa.attribute_value where pa.attribute_type_id = 'Weight'),
pi.eye_color = (select pa.attribute_value where pa.attribute_type_id = 'EyeColor')
from
#people_info pi
inner join dbo.HR.person_attributes pa on pi.person_id = pa.person_id
select * from #people_info
Which of course does not work for some reason.
If I query the two joined tables and select "pa.attribute_value where pa.attribute_type_id = 'someval'" I get the correct value. But the update does not work.
Of course, I can write this as three updates, but I am thinking that it will be faster to do one join and then to filter in the update clause.
Also, please keep in mind that my attributes are spread over three tables, not just the attributes table. So, this is why I have the table variable.
Any help is very welcome. Maybe I am going about this the wrong way. Performance matters. What is the most performant way to accomplish this?
Thank you very much.

Try this code for update with pivot:
update
pi
set
pi.height = pa.Height
pi.weight = pa.Weight
pi.eye_color = pa.EyeColor
from
#people_info pi
inner join
(
SELECT
person_id
,[Height] Height
,[Weight] Weight
,[EyeColor] EyeColor
FROM
(
SELECT
attribute_type_id
, attribute_value
, person_id
FROM
dbo.HR.person_attributes pa
) pa
PIVOT
(
MAX(attribute_value) FOR attribute_type_id IN ([Height],[Weight],[EyeColor])
)pvt
) pa
on
pi.person_id = pa.person_id

Maybe you want something like:
update pi
set
pi.height = paH.attribute_value,
pi.weight = paW.attribute_value,
pi.eye_color = paE.attribute_value
from
#people_info pi
inner join dbo.HR.person_attributes paH on pi.person_id = paH.person_id
and paH.attribute_type_id = 'Height'
inner join dbo.HR.person_attributes paW on pi.person_id = paW.person_id
and paW.attribute_type_id = 'Weight'
inner join dbo.HR.person_attributes paE on pi.person_id = paE.person_id
and paE.attribute_type_id = 'EyeColor'

Related

PostgreSQL Populate Column with Data

I try to populate null-rows in a table with data from the same table. Here is my code:
create table public.testdata(
id INTEGER,
person INTEGER,
name varchar(10));
insert into testdata (id, person,name) VALUES ( 1,1,'Jane' ), ( 2,1,'Jane' ), ( 3,1,NULL ), ( 4,2,'Tom' ), ( 5,2,NULL );
select * from testdata;
enter image description here
Basically i would like to have name 'Jane' in the 3rd row and name 'Tom' in the 5th.
Here is the asnswer which i have found online to a simmilar problem:
Update testdata
SET name = COALESCE(a1.name, b1.name)
FROM testdata a1
JOIN testdata b1
on a1.person = b1.person
and a1.id <> b1.id
where a1.name is NULL;
But if i run this code, i get name 'Jane' in every column, which is not what i want. I appreciate any help and suggestions.
Example for you:
select t1.id, t1.person, t2.name from testdata t1
left join
(
select distinct person, name from testdata
where name is not null
) t2 on t1.person = t2.person
Get the person (id?) and the desired name via a CTE. Then use the results to update names. So (see demo):
with namer (person, name) as
( select distinct on (person)
person, name
from testdata
where name is not null
order by person, name
)
update testdata d1
set name = (select n1.name
from namer n1
where n1.person = d1.person
)
where d1.name is null;
NOTE: Demo contains additional rows where the entry sequence of the rows is not ideal. And not all person values have associated name.

Need to convert Oracle "merge into" query to PostgreSQL

I'm trying to convert the following Oracle query to PostgreSQL:
MERGE into feepay.TRPT_W2_REPORTS TRPT1
USING(
WITH RWS AS
(SELECT PROG.BINCLIENT, TRPT.PUT_DIRECTORY
FROM feepay.program2 PROG
INNER JOIN feepay.TRPT_W2_PROGRAMS TRPT
ON (PROG.BINCLIENT = TRPT.BINCLIENT OR PROG.ISSUER_ID = TRPT.ISSUER_ID))
SELECT TCI.CUSTOMERNAME AS ACCOUNT,
TC.CUSTOMER_ID AS urn,
TC.LAST_NAME,
TC.FIRST_NAME,
TC.DOB,
TCA.ADDRESS
FROM feepay.TAU_CARDNUMBERS TCN
INNER JOIN feepay.TAU_CUSTOMER_CARDNUMBER TCCN ON (TCN.CARDNUMBER_ID = TCCN.CARDNUMBER_ID)
INNER JOIN feepay.TBLCUSTOMERS TC ON (TCCN.CUSTOMER_ID = TC.CUSTOMER_ID)
LEFT JOIN feepay.tau_customeraddress TCA ON (TC.CUSTOMER_ID = TCA.CUSTOMER_ID)
INNER JOIN feepay.TAU_ISSUER TI ON (TI.ISSUER_ID = TCN.ISSUER_ID)
INNER JOIN feepay.TBLCUSTOMERS TCI ON (TCI.CUSTOMER_ID = TI.CUSTOMER_ID)
LEFT JOIN feepay.TRPT_W2_REPORTS TRPT ON (TRPT.URN = TC.CUSTOMER_ID)
WHERE BINCLIENT IN (SELECT BINCLIENT FROM RWS)
AND TC.CUSTOMERNAME NOT IN ('freepay card','svds card')) TRPT2
ON (TRPT1.URN = TRPT2.URN)
WHEN MATCHED THEN
UPDATE SET
TRPT1.ACCOUNT = TRPT2.ACCOUNT,
TRPT1.LAST_NAME = TRPT2.LAST_NAME,
TRPT1.FIRST_NAME = TRPT2.FIRST_NAME,
TRPT1.DOB = TRPT2.DOB,
TRPT1.ADDRESS = TRPT2.ADDRESS,
TRPT1.LAST_UPDATE = now(),
TRPT1.STATUS = 'u' /* uPDATED */
WHEN NOT MATCHED THEN
INSERT (ACCOUNT, URN, LAST_NAME, FIRST_NAME, ISENTITY, DOB, ADDRESS, LAST_UPDATE, STATUS)
VALUES (TRPT2.ACCOUNT, TRPT2.URN, TRPT2.LAST_NAME, TRPT2.FIRST_NAME, 'y', TRPT2.DOB, TRPT2.MIDDLE_NAME,
TRPT2.ADDRESS, now(), 'i');
Unfortunately PostgreSQL does not support MERGE, so I'm really stuck. I hope some database pro out of here can help me with this.
You can use INSERT ON CONFLICT () instead:
insert into feepay.TRPT_W2_REPORTS (ACCOUNT, URN, LAST_NAME, FIRST_NAME, ISENTITY, DOB, ADDRESS, LAST_UPDATE, STATUS)
WITH RWS AS (
SELECT PROG.BINCLIENT, TRPT.PUT_DIRECTORY
FROM feepay.program2 PROG
INNER JOIN feepay.TRPT_W2_PROGRAMS TRPT
ON (PROG.BINCLIENT = TRPT.BINCLIENT OR PROG.ISSUER_ID = TRPT.ISSUER_ID)
)
SELECT TCI.CUSTOMERNAME,
TC.CUSTOMER_ID,
TC.LAST_NAME,
TC.FIRST_NAME,
'Y'
TC.DOB,
TCA.ADDRESS,
now(),
'i'
FROM feepay.TAU_CARDNUMBERS TCN
INNER JOIN feepay.TAU_CUSTOMER_CARDNUMBER TCCN ON (TCN.CARDNUMBER_ID = TCCN.CARDNUMBER_ID)
INNER JOIN feepay.TBLCUSTOMERS TC ON (TCCN.CUSTOMER_ID = TC.CUSTOMER_ID)
LEFT JOIN feepay.tau_customeraddress TCA ON (TC.CUSTOMER_ID = TCA.CUSTOMER_ID)
INNER JOIN feepay.TAU_ISSUER TI ON (TI.ISSUER_ID = TCN.ISSUER_ID)
INNER JOIN feepay.TBLCUSTOMERS TCI ON (TCI.CUSTOMER_ID = TI.CUSTOMER_ID)
LEFT JOIN feepay.TRPT_W2_REPORTS TRPT ON (TRPT.URN = TC.CUSTOMER_ID)
WHERE BINCLIENT IN (SELECT BINCLIENT FROM RWS)
AND TC.CUSTOMERNAME NOT IN ('freepay card','svds card')) TRPT2
ON CONFLICT (URN)
DO UPDATE SET
ACCOUNT = excluded.ACCOUNT,
LAST_NAME = excluded.LAST_NAME,
FIRST_NAME = excluded.FIRST_NAME,
DOB = excluded.DOB,
ADDRESS = excluded.ADDRESS,
LAST_UPDATE = now(),
STATUS = 'u' /* uPDATED */
You need to verify if the columns in the SELECT list match the columns as listed in the INSERT column list.

How to insert data with select query in postgresql?

How to insert data with select query sum? I tried. The result is affected success > Affected rows: 1. but row no add in the table.
CREATE TABLE Clases(Segment1_ID char(4), Segment1_Name varchar(75), CompanyID char(4), amount Float(53));
INSERT INTO Clases (Segment1_ID, Segment1_Name, CompanyID, amount)
select Segment1_ID, Segment1_Name, dataupload1h_companyid CompanyID, sum(case when Segment3_Formula = '+' then dataupload1d_amount else dataupload1d_amount * -1 end) Amount
from t_dataupload1_header
inner join t_dataupload1_detail
on dataupload1h_id = dataupload1d_id
inner join M_Account
on dataupload1d_accountid = Account_ID
and dataupload1h_companyid = Account_CompanyID
inner join M_Segment4
on Account_Segment4ID = Segment4_ID
and dataupload1h_companyid = Segment4_CompanyID
inner join M_Segment3
on Segment4_Segment3ID = Segment3_ID
and dataupload1h_companyid = Segment3_CompanyID
inner join M_Segment2
on Segment3_Segment2ID = Segment2_ID
and dataupload1h_companyid = Segment2_CompanyID
inner join M_Segment1
on Segment2_Segment1ID = Segment1_ID
and dataupload1h_companyid = Segment1_CompanyID
where dataupload1h_companyid = '1000'
group by Segment1_ID, Segment1_Name, dataupload1h_companyid
order by Segment1_ID, Segment1_Name, dataupload1h_companyid;
Please help. Thank you
If the select is returning the desired results then check whether Auto-commit is turned on in PgAdmin(I suppose you are using this) Or put COMMIT; after the insert.

linq subquery join and group by

Hi is it possible to translate the below queries into linq ? ...
I am using entity frameworks core and tried to use the stored procedure but it seems like i have to create a model that applies to the metadata of the stored procedure. So i am trying to understand whether this kinda such query can be translated into linq so i don't have to create a separate db model.
SELECT
Stock.stockID ProductID,
stockName ProductName,
categoryName ProductCategory,
typeName ProductType,
sizeName ProductSize,
currentQuantity CurrentQuantity,
standardQuantity QuantityPerBox,
CONVERT(VARCHAR(255),CONVERT(INT,Stock.price)) AvgUnitCost,
CONVERT(VARCHAR(255),CONVERT(INT,x.lastUnitCost)) LastOrderUnitCost,
CONVERT(VARCHAR(10),CONVERT(DATE,x.lastOrderDate)) LastOrderDate
FROM dbo.Stock
LEFT JOIN
(
SELECT stockID,unitPrice lastUnitCost ,orderDate lastOrderDate, ROW_NUMBER() OVER (PARTITION BY stockID ORDER BY orderDate DESC) rn FROM dbo.SalesOrder
JOIN dbo.SalesOrderDetail ON SalesOrderDetail.salesOrderID = SalesOrder.salesOrderID
WHERE customerID = #customerID AND salesStatus = 'S'
) x ON x.stockID = Stock.stockID AND rn = 1
LEFT JOIN dbo.StockCategory ON StockCategory.stockCategoryID = Stock.stockCategoryID
LEFT JOIN dbo.StockType ON StockType.stockTypeID = Stock.stockTypeID
LEFT JOIN dbo.StockSize ON StockSize.stockSizeID = Stock.stockSizeID
WHERE disStock = 0
Almost everything is possible. you just need to be careful with performance.
var query =
from stock in db.Stocks
join x in (
from grp in (
from so in db.SalesOrders
join sod in db.SalesOrderDetails on so.SalesOrderId equals sod.SalesOrderId
where so.CustomerId == customerId && so.SalesStatus == "S"
orderby so.OrderDate descending
select new {
sod.StockId,
LastUnitCost = sod.UnitPrice,
LastOrderDate = so.OrderDate
} into inner
group inner by inner.StockId)
select grp.Take(1)) on x.StockId equals stock.StockId into lastStockSales
from x in lastStockSales.DefaultIfEmpty()
join sc in db.StockCategories on stock.StockCatergotyId equals sc.StockCategoryId into scLeft
from sc in scLeft.DefaultIfEmpty()
join st in db.StockTypes on stock.StockTypeId equals st.StockTypeId into stLeft
from st in stLeft.DefaultIfEmpty()
join ss in db.StockSizes on stock.StockSizeId equals ss.StockSizeId into ssLeft
from ss in ssLeft.DefaultIfEmpty()
where stock.DisStock == 0
select new MyDTO {
ProductId = stock.StockId,
ProductName = stock.StockName,
ProductType = st.TypeName,
ProductSize = ss.SizeName,
CurrentQuantity = stock.CurrentQuantity,
QuantityPerBox = stock.StandardQuantity,
AvgUnitCost = stock.Price,
LastOrderUnitCost = x.LastUnitCost,
LastOrderDate = x.LastOrderDate
};
As you can see is easy to rewrite these queries, I had to change a little bit the logic on how to get the latest sales for a stock item since ROW_NUMBER() OVER (PARTITION... is not supported from a LINQ perspective. Again, you would have to consider performance when rewriting queries.
Hope this helps.

Postgres get rows which hasnt match in other table

I need your help. I need an advanced Query to my database. Im showing part of my database following:
Place (id, name, address)
Local (id, place_id, name)
PlaceReservation(id, local_id, date)
Media_Place (id, place_id, type)
Now I need a query, which gets all places with logo, which have AT LEAST ONE local which hasn't been reserved on a specific day e.g: 2015-07-01.
Help me please, because I haven't an idea how to do it. I thought about an outer join but I don't know how use it.
I was trying by:
$query = 'SELECT DISTINC *,
(SELECT sum(po.rating)/count(po.id)
FROM "Place_Opinion" po
WHERE po.place_id = p.id AND po.deleted = false) AS rating,
mp.path as logo_path
FROM "Place" p
INNER JOIN "Media_Place" mp ON mp.place_id = p.id
JOIN Local ON Local.place_id = Place.id
LEFT JOIN (
SELECT id AS rr, local_id
FROM PlaceReservation
WHERE date_start = \'2015-07-01\') Reserved ON Reserved.local_id = Local.id
WHERE mp.type = ' . Model_Row_MediaPlace::LOGO_TYPE . '
AND mp.deleted = false
AND p.deleted = false
AND rr IS NULL';
Looking for things that do not exist in a database is usually very inefficient. But you can change the logic around by finding places that do have a booking for the specified date, then LEFT JOIN that to all places with a logo and filter out the records with a reservation:
SELECT DISTINCT p.*, po.rating, mp.path as logo_path
FROM "Place" p
JOIN "Media_Place" mp ON mp.place_id = p.id AND mp.deleted = false AND mp.type = ?
JOIN Local ON Local.place_id = p.id
LEFT JOIN (
SELECT id AS rr, local_id
FROM PlaceReservation
WHERE date_start = '2015-07-01') reserved ON reserved.local_id = Local.id
LEFT JOIN (
SELECT place_id, avg(rating) AS rating
FROM "Place_Opinion"
WHERE deleted = false
GROUP BY place_id) po ON po.place_id = p.id
WHERE p.deleted = false
AND reserved.rr IS NULL;
The average rating per places is calculated in a separate sub-query. The error you had was because you referenced the "Place" table (p.id) before it was defined. For simple columns you can do that, but for sub-queries you can't.