Joining different values in columns, within the same table, into one row - sql

I currently have a table in the following format and with the following example data:
transaction name receipt
1 jim aisjdy
2 john uuyjskl
2 john ioxiqo
3 will eurique
1 jim xoqiel
3 will sijcjg
Each transaction is associated with the customer's name and they receive two separate receipts.
How would one go about creating a query that would display 'transaction', 'name' and the two different receipts in one row?
Example:
transaction name receipt receipt
1 jim aisjdy xoqiel
I know this is bad practice and, in an idea world, I'd have created two columns containing the two different receipts and/or normalized the table into two tables containing transaction and name in one, and receipt 1 and 2 in another. Unfortunately, I did not produce this table and I do not have the time to restructure everything.
I would kindly appreciate any help - thanks in advance!

Since there are only two values, you can use group by:
select transaction, name, min(receipt), max(receipt)
from t
group by transaction, name

It's not pretty, but something like this would work:
SELECT
tr1.transaction,
tr1.name,
tr1.receipt AS receipt1,
tr2.receipt AS receipt2
FROM transaction tr1
JOIN transaction tr2 ON tr1.transaction = tr2.transaction
AND tr1.receipt < tr2.receipt
This works only if there are exactly two records for each transaction, but it would also work if you knew there were always any specific number (you would just join and make sure that tr2.receipt < tr3.receipt, etc). However, as long as there are only two, Gordon's answer is easier to understand and almost certainly faster.

Related

SQL Best way to return data from one table along with mapped data from another table

I have the following problem.
I have a table Entries that contains 2 columns:
EntryID - unique identifier
Name - some name
I have another EntriesMapping table (many to many mapping table) that contains 2 columns :
EntryID that refers to the EntryID of the Entries table
PartID that refers to a PartID in a seprate Parts table.
I need to write a SP that will return all data from Entries table, but for each row in the Entries table I want to provide a list of all PartID's that are registered in the EntriesMapping table.
My question is how do I best approach the deisgn of the solution to this, given that the results of the SP would regularly be processed by an app so performance is quite important.
1.
Do I write a SP that will select multiple rows per entry - where if there are more than one PartID's registered for a given entry - I will return multiple rows each having the same EntryID and Name but different PartID's
OR
2.
Do I write a SP that will select 1 row per entry in the Entries table, and have a field that is a string/xml/json that contains all the different PartID's.
OR
3. There is some other solution that I am not thinking of?
Solution 1 seems to me to be the better way to go, but I will be passing lots of repeating data.
Solution 2 wont pass extra data, but the string/json/xml would need to be processed additionally, resuling in larger cpu time per item.
PS: I feel like this is quite a common problem to solve, but I was unable to find any resource that can provide common solutions or some pros/cons to different approaches.
I think you need simple JOIN:
SELECT e.EntryId, e.Name, em.PartId
FROM Entries e
JOIN EntriesMapping em ON e.EntryId = em.EntryId
This will return what you want, no need for stored procedure for that.

How to force ID column to remain sequential even if a recored has been deleted, in SQL server?

I don't know what is the best wording of the question, but I have a table that has 2 columns: ID and NAME.
when I delete a record from the table the related ID field deleted with it and then the sequence spoils.
take this example:
if I deleted row number 2, the sequence of ID column will be: 1,3,4
How to make it: 1,2,3
ID's are meant to be unique for a reason. Consider this scenario:
**Customers**
id value
1 John
2 Jackie
**Accounts**
id customer_id balance
1 1 $500
2 2 $1000
In the case of a relational database, say you were to delete "John" from the database. Now Jackie would take on the customer_id of 1. When Jackie goes in to check here balance, she will now show $500 short.
Granted, you could go through and update all of her other records, but A) this would be a massive pain in the ass. B) It would be very easy to make mistakes, especially in a large database.
Ids (primary keys in this case) are meant to be the rock that holds your relational database together, and you should always be able to rely on that value regardless of the table.
As JohnFx pointed out, should you want a value that shows the order of the user, consider using a built in function when querying.
In SQL Server identity columns are not guaranteed to be sequential. You can use the ROW_NUMBER function to generate a sequential list of ids when you query the data from the database:
SELECT
ROW_NUMBER() OVER (ORDER BY Id) AS SequentialId,
Id As UniqueId,
Name
FROM dbo.Details
If you want sequential numbers don't store them in the database. That is just a maintenance nightmare, and I really can't think of a very good reason you'd even want to bother.
Just generate them dynamically using tSQL's RowNumber function when you query the data.
The whole point of an Identity column is creating a reliable identifier that you can count on pointing to that row in the DB. If you shift them around you undermine the main reason you WANT an ID.
In a real world example, how would you feel if the IRS wanted to change your SSN every week so they could keep the Social Security Numbers sequential after people died off?

SQL query using fn_Split to find multiple values in column

This is a followup to my earlier question which got me part of the way to the goal.
Here is what I'm starting with:
SELECT * FROM MyTable WHERE County IN (SELECT value From dbo.fn_Split(#counties, ','))
Here's the scenario: In my table I've got a column named County. Each record can have multiple counties in the County column delimited with commas (I know this is bad form, I didn't do it). For example: county1, county22, county41. Some records may have only one county (say county13) others might have all the counties. So: county1, count2, county3... through county45 (yes, it's terrible, I know).
In the app I'm trying to build users can select multiple counties or even all counties in the same format as above (county1, county2, county3...). Thanks to Martin's help in the previous question I can get it to return records that have each of the counties individually but not the records that might contain all of the counties.
For example: The user selects county4, county26. I need to have the records returned that have just county4 and county26 as well as any that might contain both of them as part of a larger set (like all 45 of them).
Hope this is clear and I didn't make it more convoluted than necessary. Any assistance is very, very, very much appreciated!
To illustrate:
County
Record1 county1, county14, county26
Record2 county14
Record3 county1, county2, ... through county45
User Submission: county1, county26
Returns: Record1 and Record3
Not sure if I understood the question, but here is how I interpret it:
You need to return rows from your table for one or more selected items.
Also, you want to be able to select ALL items at once without passing the whole list.
I'd do that using Stored Procedure with 2 parameters:
#Selection SMALLINT
#TVP_County CountyTableType (It is Table Valued Variable. See: https://msdn.microsoft.com/en-us/library/bb510489.aspx)
If #Selection = 1 then you join #TVP_County with your table to get results.
If #Selection = 0 you return ALL records from your table w/o join and do not use #TVP_County at all.
If #Selection = -1 then you exclude #TVP_County items from your table. In that case you will be able to do a reverse marks. User will be able to select just few counties, which he/she does not want to see.
Of cause, within a stored procedure you have to implement the logic to run three different queries depending on the first parameter.

What is a fast way of joining two tables and using the first table column to "filter" the second table?

I am trying to develop a SQL Server 2005 query but I'm being unsuccessful at the moment. I trying every different approach that I know, like derived tables, sub-queries, CTE's, etc, but I couldn't solve the problem. I won't post the queries I tried here because they involve many other columns and tables, but I will try to explain the problem with a simpler example:
There are two tables: PARTS_SOLD and PARTS_PURCHASED. The first contains products that were sold to customers, and the second contains products that were purchased from suppliers. Both tables contains a foreign key associated with the movement itself, that contains the dates, etc.
Here is the simplified schema:
Table PARTS_SOLD:
part_id
date
other columns
Table PARTS_PURCHASED
part_id
date
other columns
What I need is to join every row in PARTS_SOLD with a unique row from PARTS_PURCHASED, chose by part_id and the maximum "date", where the "date" is equal of before the "date" column from PARTS_PURCHASED. In other words, I need to collect some information from the last purchase event for the item for every event of selling this item.
The problem itself is that I didn't find a way of joining the PARTS_PURCHASED table with PARTS_SOLD table using the column "date" from PARTS_SOLD to limit the MAX(date) of the PARTS_PURCHASED table.
I could have done this with a cursor to solve the problem with the tools I know, but every table has millions of rows, and perhaps using cursors or sub-queries that evaluate a query for every row would make the process very slow.
You aren't going to like my answer. Your database is designed incorrectly which is why you can't get the data back out the way you want. Even using a cursor, you would not get good data from this. Assume that you purchased 5 of part 1 on May 31, 2010. Assume on June 1, you sold ten of part 1. Matching just on date, you would match all ten to the May 31 purchase even though that is clearly not correct, some parts might have been purchased on May 23 and some may have been purchased on July 19, 2008.
If you want to know which purchased part relates to which sold part, your database design should include the PartPurchasedID as part of the PartsSold record and this should be populated at the time of the purchase, not later for reporting when you have 1,000,000 records to sort through.
Perhaps the following would help:
SELECT S.*
FROM PARTS_SOLD S
INNER JOIN (SELECT PART_ID, MAX(DATE)
FROM PARTS_PURCHASED
GROUP BY PART_ID) D
ON (D.PART_ID = S.PART_ID)
WHERE D.DATE <= S.DATE
Share and enjoy.
I'll toss this out there, but it's likely to contain all kinds of mistakes... both because I'm not sure I understand your question and because my SQL is... weak at best. That being said, my thought would be to try something like:
SELECT * FROM PARTS_SOLD
INNER JOIN (SELECT part_id, max(date) AS max_date
FROM PARTS_PURCHASED
GROUP BY part_id) AS subtable
ON PARTS_SOLD.part_id = subtable.part_id
AND PARTS_SOLD.date < subtable.max_date

Search across Columns and replace text

I have an Access database of information where I need to replace text that may reside in 1 of 10 columns. I have a number of different requests for find and replace that need to be done. I need to do this twice a day.
These are the details. We receive a download of data twice a day that has course information in it. A record can have 10 courses in it. Some of the courses need to be combined. For instance
Course 1 is 12345, there are 2 other courses that are the same and therefore course 2(01234), Course 3(34566) all need to be changed to 12345. I also need to combine other course in a similar fashion, since I need to do this twice a day, ideally I would like to have a table with just columns of find and replace and use it to pick up the changes and reference it in my sql code.
An easy way to do this is the key!
Have you considered a cross reference table of something like
Table1
MCourse Subcourse
12345 2(01234)
12345 3(34566)
Then you can do updates like
Set mainTable.Desiredfield = Table1.Mcourse
where desiredfield = subcourse
Or you can create a query that uses the cross reference table to select the desired value and make a new table from that.