Adding multiple rows of data into one - TSQL - sql

I'm currently working on a project of my own that uses an SQL DB to store character information for an RPG game. I'm trying to write a select statement to retrieve the information related to the equipment the user is using, at the moment I have:
SELECT
CE.CharacterID, CE.EquipmentSlotID, CE.ItemID, I.ItemTypeID, I.Name, I.Image, IT.Name, IT.Description, concat(IA.AttributeID, ' ', IA.value )
FROM
CharacterEquipment CE INNER JOIN Item I
ON CE.ItemID = I.ItemID
INNER JOIN ItemType IT
ON I.ItemTypeID=IT.ItemTypeID
INNER JOIN ItemAttribute IA
ON I.ItemID=IA.ItemID
WHERE CE.CharacterID = 1
ORDER BY CE.ItemID ASC;
This returns this:
My problem is that each item (ItemID) can have multiple attributes (AttributeID) and I want all these attributes to appear on a single line but I'm not sure how I'd go about combining the results
EDIT:
Sorry guys, should have said, I've been experimenting with the group concat function but SQL isnt my strong suit so im having bother writing it and figuring out how it works, any chance any one could explain it abit more indepth? Thanks

Possible solution to this would be improving your database structure and try denormalizing it.
2.But if you wish to continue with same structure try using Concatenation using COALESCE function
For your reference :Concatenate many rows to one

Related

Having troubles with a conditional count in SQL

I'm working on an SQL project (involving a library database) and I'm having a hard time figuring out how to make a conditional count.
So, I have 4 tables: Imprumuturi, Cititori, Autori, Carti. I need to list the 'Cititori' that have more than one borrowed 'Carti' at the current time.
I tried to use
SELECT cititori.nume_cititor,COUNT(imprumuturi.pk_cititor)
AS numar_imprumuturi FROM cititori, imprumuturi
WHERE imprumuturi.data_return IS NULL GROUP BY cititori.nume_cititor
HAVING COUNT(imprumuturi.pk_cititor)>1
ORDER BY cititori.nume_cititor;
And while it lists all the 'Cititori", it doesn't count the number of active borrowed 'Carti' as it should.
Can I get a hint or some help on how to make it work?
These are the fields of my database
Seems you missed the relation between the tables:
SELECT cititori.nume_cititor,COUNT(imprumuturi.pk_cititor)
AS numar_imprumuturi
FROM cititori
INNER JOIN imprumuturi ON imprumuturi.pk_cititori = cititori.pk_cititori
WHERE imprumuturi.data_return IS NULL
GROUP BY cititori.nume_cititor
HAVING COUNT(imprumuturi.pk_cititor)>1
ORDER BY cititori.nume_cititor;
As suggested, you should not use the old implicit join syntax based on comma-separated table names and where condition, but use explicit join syntax.

How can I write a SQL query to display ID, First Name, Last Name of all players with more than 2000 career hits based on schema

I am new to SQL and DB management. I am working on writing queries based on a schema which you can find below. This is an exercise for me to get familiar reading, writing queries on SQL Server for my job. Could you please help me out defining query based on the schema and simply explain the logic?
Thanks a lot!
SQL Server is my DBMS and here are the question
Display ID, First Name, Last Name, and Hits to display all players with more than 2000 career hits.
This one you can get by typing this query in Microsoft SQL Server
SELECT
MLB_PLAYERS.FIRST_NAME,
MLB_PLAYERS.LAST_NAME,
MLB_PLAYERS.ID,
CAREER_STATS.HITS
FROM
MLB_PLAYERS LEFT JOIN KEY_GAMES_STATS on MLB_PLAYERS.ID=CAREER_STATS.ID
WHERE
CAREER_STATS.HITS>2000
So you have a simple structure to follow:
SELECT
FROM
WHERE
GROUP BY
HAVING
ORDER BY
But you decide to get only 3 of them, which is select, from and where. By SELECT you decide which columns you wanna have as an output. Then in FROM you have to choose tables from which you wanna take your variables. But if you decide to use 2 different tables you need to join them. I used left join because I wanted to match hits to existing players. We can match them by similar key, in this case this is their ID. And eventually, you can use where to apply conditions to your queries
I guess you could do it with a join and a group
select p.MLB_PLAYERS.FIRST_NAME,
p.MLB_PLAYERS.LAST_NAME,
p.MLB_PLAYERS.ID,
count(g.KEY_GAMES_STATS.HITS) as hits
from MLB_PLAYERS p
left join KEY_GAMES_STATS on p.ID = g.ID -- not sure how to link there 2 tables
group by p.MLB_PLAYERS.FIRST_NAME,
p.MLB_PLAYERS.LAST_NAME,
p.MLB_PLAYERS.ID
having count(g.KEY_GAMES_STATS.HITS) > 2000

multiple result query as csv string in new column

I ve got 3 tables:
User, product user2product
each product has got ID.
Is it possible to write query in which as result I would get 2 columns:
UserID, Products
And in products column I have all products connected to user by user2product table separated with comma.
what SQL-Dialect are we talking about?
For postgresql, there is a pretty comprehensive answer to be found here:
How to concatenate strings of a string field in a PostgreSQL 'group by' query?
For other SQL-Systems the idea should be pretty much the same. You would have to search for the correct aggregate function, everything else can be done using a simple group by directive.
Cheers
Thilo
Its quite possible to do this using in native tsql using the for xml path statement as detailed here
To access this through linq create a stored procedure on the db, drag it into your dbml file and then call it from the data context like a method.
The code for the sp would look something like this
select
u.userid,
substring(( select ',' + cast(p.productid as nvarchar(20))
from
user2product up inner join
product p on up.productid = p.productid
where
up.userid = u.userid
for xml path('')),2,2000000) as Products
from
users u

Speeding up a SQL query with generic data information

Due to a variety of design decisions, we have a table, 'CustomerVariable'. CustomerVariable has three bits of information--its own id, an id to Variable (a list of possible settings the customer can have), and the value for that variable. The Variable table, on the other hand, has the information on a default--in case the CustomerVariable is not set.
This works well in most situations, allowing us not to have to create an insanely long list of information--especially in a case where there are 16 similar variables that need to be handled for a customer.
The problem comes in trying to get this information into a select. So far, our 'best' solution involves far too many joins to be efficient--we get a list of the 16 VariableIds we need information on, setting them into variables, first. Later on, however, we have to do this:
CROSS JOIN dbo.Variable v01
LEFT JOIN dbo.CustomerVariable cv01 ON cv01.customerId = c.id
AND cv01.variableId = v01.id
CROSS JOIN dbo.Variable v02
LEFT JOIN dbo.CustomerVariable cv02 ON cv02.customerId = c.id
AND cv02.variableId = v02.id
-- snip --
CROSS JOIN dbo.Variable v16
LEFT JOIN dbo.CustomerVariable cv16 ON cv16.customerId = c.id
AND cv16.variableId = v16.id
WHERE
v01.id = #cv01VariableId
v02.id = #cv02VariableId
-- snip --
v16.id = #cv16VariableId
I know there has to be a better way, but we can't seem to find it amidst crunch time. Any help would be greatly appreciated.
If your data set is relatively small and not too volatile, you may want to use materialized views (assuming your database supports them) to optimize the lookup.
If materialized views are not an option, consider writing a stored procedure that retrieves that data in two passes:
First retrieve all of the CustomerVariables available for a particular customer (or set of customers)
Next, retrieve all of the default values from the Variables table
Perform a non-distinct union on the results merging the defaults in wherever a CustomerVariable record is missing.
Essentially, this is the equivalent of:
SELECT variableId,
CASE WHEN CV.variableId = NULL THEN VR.defaultValue ELSE CV.value END
FROM Variable VR
LEFT JOIN CUstomerVariable CV on CV.variableId = VR.variableId
WHERE CV.customerId = c.id
The type of query you want is called a pivot table or crosstab query.
By far the easiest way of dealing with this is to create a view based off of a crosstab query. This will flip the columns from being vertical to being horizontal like a regular sql table. Once that is done, just query the view. Easy. ;)

Object Relational Mapping Issues: Suggestions needed

I've been trying to come up with a good design pattern for mapping data contained in relational databases to the business objects I've created but I keep hitting a wall.
Consider the following tables:
TYPE: typeid, description
USER: userid, username, usertypeid->TYPE.typeid, imageid->IMAGE.imageid
IMAGE: imageid, location, imagetypeid->TYPE.typeid
I would like to gather all the information regarding a specific user. Creating a query for this isn't too difficult.
SELECT u.*, ut.*, i.*, it.* FROM user u
INNER JOIN type ut ON ut.typeid = u.usertypeid
INNER JOIN image i ON i.imageid = u.imageid
INNER JOIN type it ON it.typeid = i.imagetypeid
WHERE u.userid = #userid
The problem is that the field names collide and then I'm forced to alias every single field which gets out of hand very quickly.
Does anyone have a decent design pattern for this kind of thing?
I've thought about retrieving multiple results from a single stored procedure and then using a dataset to iterate through each one but I'm worried that some performance issues might bite me in the butt later. For example instead of the above query something like:
SELECT u.*, t.* FROM user u
INNER JOIN type t ON t.typeid = u.usertypeid
WHERE u.userid = #userid;
SELECT i.*, t.* FROM image i
INNER JOIN type t ON t.typeid = i.imagetypeid
INNER JOIN user u ON u.imageid = i.imageid
WHERE u.userid = #userid;
Does that seem like a decent solution? Can anyone foresee any issues with this approach?
Never use the SQL * wildcard in production code. Always spell out all the columns you want to retrieve.
Then aliasing some of them doesn't seem like such a huge amount of extra work.
Re your comment asking for background and reasoning:
Sometimes you don't really need every column from all tables, and fetching them can be needlessly costly (especially for large strings and blobs). There is no SQL syntax for "all columns except the following exceptions."
You can't alias columns that you fetch using the wildcard. Once you need to alias any of the columns, you need to expand the wildcard to list all the columns explicitly.
If the table structure changes, e.g. columns are renamed, reordered, dropped, or added, then the wildcard fetches them all, by position as defined in the tables. This may seem like a convenience, but not when your application depends on columns being in the result set by a given name or in a given position. You can get mysterious bugs where your application displays columns in the wrong order (if referencing columns by position), or shows them as blank (if referencing columns by name).
However, if the SQL query names columns explicitly, you can employ the "Fail Early" principle. This helps debugging, because it leads you directly to the SQL query that needs to be edited to account for the schema change.